New Upstream Snapshot - python-braintree

Ready changes

Summary

Merged new upstream version: 4.18.0 (was: 4.17.1).

Resulting package

Built on 2023-01-20T15:53 (took 6m11s)

The resulting binary packages can be installed (if you have the apt repository enabled) by running one of:

apt install -t fresh-snapshots python3-braintree

Lintian Result

Diff

diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
deleted file mode 100644
index 2654cd8..0000000
--- a/.github/CODEOWNERS
+++ /dev/null
@@ -1 +0,0 @@
-* @braintree/sdk
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
deleted file mode 100644
index cb3a1ee..0000000
--- a/.github/ISSUE_TEMPLATE.md
+++ /dev/null
@@ -1,9 +0,0 @@
-### General information
-
-* SDK/Library version: <!-- Example: 3.37.0 -->
-* Environment: <!-- Is this issue in Sandbox or Production? -->
-* Language, language version, and OS: <!-- Example: Python 2.7.0 on Ubuntu 16.10 -->
-
-### Issue description
-
-<!-- To help us quickly reproduce your issue, include as many details as possible, such as logs, steps to reproduce, and so on.  If the issue reports a new feature, follow the [user story](https://en.wikipedia.org/wiki/User_story) format to clearly describe the use case. -->
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
deleted file mode 100644
index de9aa9e..0000000
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ /dev/null
@@ -1,6 +0,0 @@
-# Summary
-
-# Checklist
-
-- [ ] Added changelog entry
-- [ ] Ran unit tests (`nosetests tests/unit`)
diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index e4b4fae..0000000
--- a/.gitignore
+++ /dev/null
@@ -1,7 +0,0 @@
-*.pyc
-/dist
-/docs/_build
-/tags
-MANIFEST
-build
-/venv
diff --git a/.pylintrc b/.pylintrc
deleted file mode 100644
index d4cb029..0000000
--- a/.pylintrc
+++ /dev/null
@@ -1,249 +0,0 @@
-[MASTER]
-
-# Specify a configuration file.
-#rcfile=
-
-# Python code to execute, usually for sys.path manipulation such as
-# pygtk.require().
-#init-hook=
-
-# Profiled execution.
-profile=no
-
-# Add files or directories to the blacklist. They should be base names, not
-# paths.
-ignore=CVS
-
-# Pickle collected data for later comparisons.
-persistent=yes
-
-# List of plugins (as comma separated values of python modules names) to load,
-# usually to register additional checkers.
-load-plugins=
-
-
-[MESSAGES CONTROL]
-
-# Enable the message, report, category or checker with the given id(s). You can
-# either give multiple identifier separated by comma (,) or put this option
-# multiple time.
-#enable=
-
-# Disable the message, report, category or checker with the given id(s). You
-# can either give multiple identifier separated by comma (,) or put this option
-# multiple time (only on the command line, not in the configuration file where
-# it should appear only once).
-disable=C0111,C0103,C0301,E1101,E1103,R0903,R0904,W0401,W0614
-
-
-[REPORTS]
-
-# Set the output format. Available formats are text, parseable, colorized, msvs
-# (visual studio) and html
-output-format=text
-
-# Include message's id in output
-include-ids=no
-
-# Put messages in a separate file for each module / package specified on the
-# command line instead of printing them on stdout. Reports (if any) will be
-# written in a file name "pylint_global.[txt|html]".
-files-output=no
-
-# Tells whether to display a full report or only the messages
-reports=yes
-
-# Python expression which should return a note less than 10 (10 is the highest
-# note). You have access to the variables errors warning, statement which
-# respectively contain the number of errors / warnings messages and the total
-# number of statements analyzed. This is used by the global evaluation report
-# (RP0004).
-evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
-
-# Add a comment according to your evaluation note. This is used by the global
-# evaluation report (RP0004).
-comment=no
-
-
-[MISCELLANEOUS]
-
-# List of note tags to take in consideration, separated by a comma.
-notes=FIXME,XXX,TODO
-
-
-[SIMILARITIES]
-
-# Minimum lines number of a similarity.
-min-similarity-lines=4
-
-# Ignore comments when computing similarities.
-ignore-comments=yes
-
-# Ignore docstrings when computing similarities.
-ignore-docstrings=yes
-
-
-[TYPECHECK]
-
-# Tells whether missing members accessed in mixin class should be ignored. A
-# mixin class is detected if its name ends with "mixin" (case insensitive).
-ignore-mixin-members=yes
-
-# List of classes names for which member attributes should not be checked
-# (useful for classes with attributes dynamically set).
-ignored-classes=SQLObject
-
-# When zope mode is activated, add a predefined set of Zope acquired attributes
-# to generated-members.
-zope=no
-
-# List of members which are set dynamically and missed by pylint inference
-# system, and so shouldn't trigger E0201 when accessed. Python regular
-# expressions are accepted.
-generated-members=REQUEST,acl_users,aq_parent
-
-
-[BASIC]
-
-# Required attributes for module, separated by a comma
-required-attributes=
-
-# List of builtins function names that should not be used, separated by a comma
-bad-functions=map,filter,apply,input
-
-# Regular expression which should only match correct module names
-module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
-
-# Regular expression which should only match correct module level names
-const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$
-
-# Regular expression which should only match correct class names
-class-rgx=[A-Z_][a-zA-Z0-9]+$
-
-# Regular expression which should only match correct function names
-function-rgx=[a-z_][a-z0-9_]{2,30}$
-
-# Regular expression which should only match correct method names
-method-rgx=[a-z_][a-z0-9_]{2,30}$
-
-# Regular expression which should only match correct instance attribute names
-attr-rgx=[a-z_][a-z0-9_]{2,30}$
-
-# Regular expression which should only match correct argument names
-argument-rgx=[a-z_][a-z0-9_]{2,30}$
-
-# Regular expression which should only match correct variable names
-variable-rgx=[a-z_][a-z0-9_]{2,30}$
-
-# Regular expression which should only match correct list comprehension /
-# generator expression variable names
-inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
-
-# Good variable names which should always be accepted, separated by a comma
-good-names=i,j,k,ex,Run,_
-
-# Bad variable names which should always be refused, separated by a comma
-bad-names=foo,bar,baz,toto,tutu,tata
-
-# Regular expression which should only match functions or classes name which do
-# not require a docstring
-no-docstring-rgx=__.*__
-
-
-[VARIABLES]
-
-# Tells whether we should check for unused import in __init__ files.
-init-import=no
-
-# A regular expression matching the beginning of the name of dummy variables
-# (i.e. not used).
-dummy-variables-rgx=_|dummy
-
-# List of additional names supposed to be defined in builtins. Remember that
-# you should avoid to define new builtins when possible.
-additional-builtins=
-
-
-[FORMAT]
-
-# Maximum number of characters on a single line.
-max-line-length=80
-
-# Maximum number of lines in a module
-max-module-lines=1000
-
-# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
-# tab).
-indent-string='    '
-
-
-[DESIGN]
-
-# Maximum number of arguments for function / method
-max-args=5
-
-# Argument names that match this expression will be ignored. Default to name
-# with leading underscore
-ignored-argument-names=_.*
-
-# Maximum number of locals for function / method body
-max-locals=15
-
-# Maximum number of return / yield for function / method body
-max-returns=6
-
-# Maximum number of branch for function / method body
-max-branchs=12
-
-# Maximum number of statements in function / method body
-max-statements=50
-
-# Maximum number of parents for a class (see R0901).
-max-parents=7
-
-# Maximum number of attributes for a class (see R0902).
-max-attributes=7
-
-# Minimum number of public methods for a class (see R0903).
-min-public-methods=2
-
-# Maximum number of public methods for a class (see R0904).
-max-public-methods=20
-
-
-[IMPORTS]
-
-# Deprecated modules which should not be used, separated by a comma
-deprecated-modules=regsub,string,TERMIOS,Bastion,rexec
-
-# Create a graph of every (i.e. internal and external) dependencies in the
-# given file (report RP0402 must not be disabled)
-import-graph=
-
-# Create a graph of external dependencies in the given file (report RP0402 must
-# not be disabled)
-ext-import-graph=
-
-# Create a graph of internal dependencies in the given file (report RP0402 must
-# not be disabled)
-int-import-graph=
-
-
-[CLASSES]
-
-# List of interface methods to ignore, separated by a comma. This is used for
-# instance to not check methods defines in Zope's Interface base class.
-ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by
-
-# List of method names used to declare (i.e. assign) instance attributes.
-defining-attr-methods=__init__,__new__,setUp
-
-# List of valid names for the first argument in a class method.
-valid-classmethod-first-arg=cls
-
-
-[EXCEPTIONS]
-
-# Exceptions that will emit a warning when being caught. Defaults to
-# "Exception"
-overgeneral-exceptions=Exception
diff --git a/ACKNOWLEDGEMENTS.md b/ACKNOWLEDGEMENTS.md
deleted file mode 100644
index 8fe0d18..0000000
--- a/ACKNOWLEDGEMENTS.md
+++ /dev/null
@@ -1,6 +0,0 @@
-Acknowledgements
-----------------
-
-The Braintree SDK uses code from the following libraries:
-
-* [requests](https://github.com/kennethreitz/requests), Apache License, Version 2.0
diff --git a/CHANGELOG.md b/CHANGELOG.md
deleted file mode 100644
index 0b3003c..0000000
--- a/CHANGELOG.md
+++ /dev/null
@@ -1,618 +0,0 @@
-## 3.57.1
-* Set correct version for PyPi
-
-## 3.57.0 
-* Forward `processor_comments` to `forwarded_comments`
-* Add Venmo 'TokenIssuance' gateway rejection reason
-* Add `AmountNotSupportedByProcessor` to validation error
-
-## 3.56.0
-* Add PayPalHere details
-* Add `networkResponseCode` and `networkResponseText` to transactions and verifications
-* Add `cavv`, `xid`, `ds_transaction_id`, `eci_flag`, and `three_d_secure_version`, to `three_d_secure_info`
-* Add `three_d_secure_info` to credit_card_verification
-* Add `GraphQLClient` to `BraintreeGateway` class
-
-## 3.55.0
-* Add `captureId` field to local_payment_details
-* Add `refundId` field to local_payment_details
-* Add `debugId` field to local_payment_details
-* Add `transactionFeeAmount` field to local_payment_details
-* Add `transactionFeeCurrencyIsoCode` field to local_payment_details
-* Add `refundFromTransactionFeeAmount` field to local_payment_details
-* Add `refundFromTransactionFeeCurrencyIsoCode` field to local_payment_details
-* Add `ds_transaction_id` and `three_d_secure_version` to 3DS pass thru fields
-* Add `payer_info` field to payment_method_nonce details
-* Add more specific timeout errors: (#105 thanks @bhargavrpatel)
-* Add `braintree.exceptions.http.timeout_error.ConnectTimeoutError` (child class of TimeoutError)
-* Add `braintree.exceptions.http.timeout_error.ReadTimeoutError` (child class of TimeoutError)
-* Add `room_tax` support for transaction sale
-* Add `no_show` support for transaction sale
-* Add `advanced_deposit` support for transaction sale
-* Add `fire_safe` support for transaction sale
-* Add `property_phone` support for transaction sale
-* Add `additional_charges` support for transaction sale
-* Add `PostalCodeIsRequiredForCardBrandAndProcessor` to validation errors
-* Fix issue where not found error could choke on `None` values (#109)
-
-## 3.54.0
-* Add `payment_method_nonce` field to `LocalPaymentCompleted` webhook
-* Add `transaction` field to `LocalPaymentCompleted` webhook
-* Add `LocalPaymentDetails` to transactions
-
-## 3.53.0
-* Add `refund_from_transaction_fee_amount` field to paypal_details
-* Add `refund_from_transaction_fee_currency_iso_code` field to paypal_details
-* Add `revoked_at` field to paypal_account
-* Add support for `PaymentMethodRevokedByCustomer` webhook
-
-## 3.52.0
-* Deprecate `GrantedPaymentInstrumentUpdate` and add `GrantorUpdatedGrantedPaymentMethod` and `RecipientUpdatedGrantedPaymentMethod`
-* Add account_type support for transaction sale, verification, and payment_method create/update
-
-## 3.51.0
-* Add Hiper card type support
-* Add Hipercard card type support
-* Add `bin` to `PaymentMethodNonceDetails`
-* Clarify support for Python versions 3.6.x and 3.7.x
-* Add Error indicating pdf uploads too long for dispute evidence.
-* Add `GrantedPaymentMethodRevoked` webhook response objects
-
-## 3.50.0
-* Add `fraud_service_provider` field to `risk_data`
-* Add `authorization_expires_at` to `Transaction`
-* Remove invalid transaction tests
-* Allow PayPal payment ID and payer ID to be passed during transaction create
-* Add `travel_flight` support to industry-specific data
-* Add `processor_response_type` to `Transaction`, `AuthorizationAdjustment`, and `CreditCardVerification`.
-
-## 3.49.0
-* Add new field `network_transaction_id` in transaction response.
-* Add `external_vault` option to transaction sale.
-* Add `LocalPaymentCompleted` webhook.
-* Add `processor_response_type` to `Transaction`, `AuthorizationAdjustment`, and `CreditCardVerification`.
-
-## 3.48.0
-* Add ID to Transaction in SubscriptionChargedSuccessfully test webhook (#99, thanks @bjackson)
-* Fix dispute results in transactions not showing the correct status sometimes
-* Add Elo card type support
-
-## 3.47.0
-* Add processor respone code and processor response text to authorization adjustments subfield in transaction response.
-* Add support for Samsung Pay
-
-## 3.46.0
-* Allow payee ID to be passed in options params for transaction create
-* Add `merchant_id` alias to ConnectedMerchantStatusTransitioned and ConnectedMerchantPayPalStatusChanged Auth webhooks
-
-## 3.45.0
-* Add support for US Bank Account verifications API
-
-## 3.44.0
-* Add Dispute error ValidEvidenceRequiredToFinalize
-
-## 3.43.0
-* Add `oauth_access_revocation` to `WebhookNotification`s
-* Add support for `customer_id`, `disbursement_date` and `history_event_effective_date` in DisputeSearch
-* Remove `sepa_mandate_type` and `sepa_mandate_acceptance_location` params from `ClientToken`
-* Add support for VCR compelling evidence dispute representment
-
-## 3.42.0
-* Add support for `association_filter_id` in `Customer#find`
-
-## 3.41.0
-* Deprecated `LineItem/DiscountAmountMustBeGreaterThanZero` error in favor of `DiscountAmountCannotBeNegative`
-* Deprecated `LineItem/UnitTaxAmountMustBeGreaterThanZero` error in favor of `UnitTaxAmountCannotBeNegative`
-* Add support for `tax_amount` field on transaction `line_items`
-* Add support for `source_merchant_id` on webhooks
-* Add `find_all` static method to `TransactionLineItem` class
-* Add support for `profile_id` in Transaction#create options for VenmoAccounts
-
-## 3.40.0
-* Add level 3 fields to Transactions:
-  * discount_amount
-  * shipping_amount
-  * ships_from_postal_code
-* Add support for transaction line items
-* Add support for tagged evidence in DisputeGateway#add_text_evidence (Beta release)
-* Update https certificate bundle
-
-## 3.39.1
-* Fix spec to expect PayPal transactions to move to settling rather than settled
-* Fix AchMandate.acceptedAt attribute parsing
-* Fix regression for `http_strategy.http_do`
-
-## 3.39.0
-* Add support for upgrading a PayPal future payment refresh token to a billing agreement
-* Fix braintree.Dispute.search to take a list of search criteria
-* Add logic to remove deprecation warnings for encodestring and decodestring when used with python 3 (#92)
-* Fix spec to expect PayPal transaction to settle immediately after successful capture
-* Add GrantedPaymentInstrumentUpdate webhook support
-* Add ability to create a transaction from a shared nonce
-* Add `options` -> `paypal` -> `shipping` for creating & updating customers as well as creating payment methods
-* Do not convert to Decimal if amount is None in AuthorizationAdjustement (#70)
-* Add `device_data_captured` field to `risk_data`
-* Add `bin_data` to `payment_method_nonce`
-
-## 3.38.0
-* Add iDEAL webhook support
-* Add AuthorizationAdjustment class and `authorization_adjustments` to Transaction
-* Coinbase is no longer a supported payment method. `PaymentMethodNoLongerSupported` will be returned for Coinbase operations
-* Add facilitated details to Transaction if present
-* Add `submit_for_settlement` option to `Subscription.retry_charge`
-* Add `options` -> `paypal` -> `description` for creating and updating subscriptions
-* Add Braintree.Dispute.find
-* Add braintree.Dispute.accept
-* Add braintree.Dispute.add_file_evidence
-* Add braintree.Dispute.add_text_evidence
-* Add braintree.Dispute.finalize
-* Add braintree.Dispute.find
-* Add braintree.Dispute.remove_evidence
-* Add braintree.Dispute.search
-* Add braintree.DocumentUpload
-
-## 3.37.2
-* Fix a bug where a null value for `amount` in `CreditCardVerification` would result in a `ValueError`
-* Add docstrings for AttributeGetter and Search. Thanks @sharma7n!
-* Add support for additional PayPal options when vaulting a PayPal Order
-
-## 3.37.1
-* Add gzip support
-* Fix a bug in CreditCardVerification where `amount` and `currency_iso_code` were always expected
-
-## 3.37.0
-* Fix a regression where `util/datetime_parser.py` was missing
-* Add support for Visa Checkout
-* Improve setup.py
-* Verification response includes amount and currency iso code
-* Add support for payee_email with paypal intent=order
-* Add support for skip_avs & skip_cvs
-
-## 3.36.0
-* Add ConnectedMerchantStatusTransitioned and ConnectedMerchantPayPalStatusChanged Auth webhooks
-
-## 3.35.0
-* Add LICENSE metadata. Thanks graingert.
-* Allow custom verification amount on payment method updates.
-* Fix a bug where `merchant_account.all` would attempt to fetch too many pages of merchant accounts
-
-##  3.34.0
-* Stop sending account_description field from us bank accounts
-* Add functionality to list all merchant accounts for a merchant with `merchant_account.all`
-
-## 3.33.0
-* Add option `skip_advanced_fraud_check` for transaction flows
-
-## 3.32.0
-* Update UsBank tests to use legal routing numbers
-* Allow setting a custom verification amount in `PaymentMethod` options
-* Allow setting processor specific fields for transactions and verifications
-
-## 3.31.0
-* Fix `UsBankAccount` support for `Customer`s
-* Added handling for unicode parameters. (Thanks @mgalgs)
-* Raise `ConfigurationError` for empty string credentials
-* Update `Grant` api to support options dictionary
-
-## 3.30.0
-* Add 'UsBankAccount' payment method
-
-## 3.29.2
-* Update links in docstrings
-* Remove Python 3.x-incompatible branch check
-* Remove references to SubMerchantAccount API
-
-## 3.29.1
-* Improve error handling around server timeouts
-
-## 3.29.0
-* Allow 'default_payment_method' option in Customer
-* Allow 'transaction_source' option in Transaction Sale
-
-## 3.28.0
-* Expose resource collection ids
-* Add order id to refund
-* Enable 3DS pass thru
-
-## 3.27.0
-* Add method of revoking OAuth access tokens
-
-## 3.26.1
-* Correct issue with setup.py
-
-## 3.26.0
-* Add Transaction `update_details`
-* Support for Too Many Requests response codes
-* Add SubMerchantAccount object with associate objects
-* Allow more parameters to be sent on SubMerchantAccount create
-* Add SubMerchantAccount update
-* Handle validation errors for SubMerchantAccount create / update
-
-## 3.25.0
-* Add AccountUpdaterDailyReport webhook parsing
-
-## 3.24.0
-* Add Verification#create
-* Add options to `submit_for_settlement` transaction flows
-* Update https certificate bundle
-* Support environment settings with strings
-
-## 3.23.0
-* Add better defaults to client token generation when using an access token by consolidating client token defaults into ClientTokenGateway
-* Add PaymentMethodGateway#revoke
-
-## 3.22.0
-* Add VenmoAccount
-* Add support for Set Transaction Context supplementary data.
-
-## 3.21.0
-* Add transaction to subscription successfully charged webhook
-* Add new ProcessorDoesNotSupportAuths error
-* Add support for partial settlement transactions
-* Add constants for dispute kind
-* Preserve backtrace when not wrapping HTTP exceptions
-* Add date_opened and date_won to dispute webhooks
-* Add support for searching transactions from oauth app
-* Support AMEX express checkout
-
-## 3.20.0
-* add source\_description to android pay and apple pay
-* add new android pay test nonces
-* add support for amex rewards transactions
-* add billing\_agreement\_id to paypalaccount
-
-## 3.19.0
-* Add new test payment method nonces
-* Allow passing description on PayPal transactions
-
-## 3.18.0
-* Fix oauth authentication
-* Fix python 3 syntax
-
-## 3.17.0
-* Add oauth support
-
-## 3.16.0
-* Add support for Android Pay
-
-## 3.15.0
-* Validate webhook challenge payload
-
-## 3.14.0
-* Add 3DS server side fields
-
-## 3.13.0
-* Add attribute to customer
-* Add coinbase constant
-* Add European test nonce
-
-## 3.12.0
-* Add support for new SEPA workflow
-
-## 3.11.1
-* Fix test failures in Python 3.3+
-
-## 3.11.0
-* Accept additional params in PaymentMethod.create()
-
-## 3.10.0
-* Add 3D Secure transaction fields
-* Add ability to create nonce from vaulted payment methods
-
-## 3.9.0
-* Support Coinbase accounts
-* Surface Apple Pay payment instrument name in responses
-* Expose subscription status events
-* Support SEPA bank accounts for customer
-* Improve documentation
-
-## 3.8.0
-* Add error code constants
-* Allow PayPal parameters to be sent in options.paypal
-
-## 3.7.0
-* Add risk_data to Transaction and Verification with Kount decision and id
-* Add verification_amount an option when creating a credit card
-* Add TravelCruise industry type to Transaction
-* Add room_rate to Lodging industry type
-* Add CreditCard#verification as the latest verification on that credit card
-* Add ApplePay support to all endpoints that may return ApplePayCard objects
-* Align WebhookTesting with other client libraries
-
-## 3.6.0
-* Allow descriptor to be passed in Funding Details options params for Merchant Account create and update.
-
-## 3.5.0
-* Add additional_processor_response to transaction
-
-## 3.4.1
-* Allow payee_email to be passed in options params for Transaction create
-
-## 3.4.0
-
-* Added paypal specific fields to transaction calls
-* Added SettlementPending, SettlementDeclined transaction statuses
-
-## 3.3.0
-
-* Add Descriptor url support
-* Fix client token version type
-
-## 3.2.0
-
-* Support credit card options and billing address in PaymentMethod.create
-* Add PaymentMethod.update
-* Add associated subscriptions to PayPalAccount
-* Test refactoring and cleanup
-
-## 3.1.1
-
-* Add support for v.zero SDKs
-
-## 3.0.0
-
-* Drop Python 2.5 support
-* Remove use_unsafe_ssl option
-* Remove httplib strategy and pycurl strategy
-* Add Python 3.3+ support
-
-## 2.29.1
-
-* Make webhook parsing more robust with newlines
-* Add messages to InvalidSignature exceptions
-
-## 2.29.0
-
-* Include Dispute information on Transaction
-* Search for Transactions disputed on a certain date
-
-## 2.28.0
-
-* Disbursement Webhooks
-
-## 2.27.0
-
-* Fix using instantiated Configuration objects without first calling Configuration.configure
-* Accept billing_address_id on transaction create
-* Expose current_billing_cycle on addons and discounts
-
-## 2.26.0
-
-* Merchant account find API
-
-## 2.25.0
-
-* Merchant account update API
-* Merchant account create API v2
-
-## 2.24.1
-* Update configuration URLs
-
-## 2.24.0
-* Add partnership support
-* Add partner configuration
-
-## 2.23.1
-* Add configuration option for custom HTTP strategies
-
-## 2.23.0
-* Adds hold_in_escrow method
-* Add error codes for verification not supported error
-* Add company_name and tax_id to merchant account create
-* Adds cancel_release methods
-* Adds release_from_escrow functionality
-* Adds owner_phone to merchant account signature.
-* Adds merchant account phone error code.
-
-## 2.22.0
-* Adds device data to transactions, customers, and credit cards.
-
-## 2.21.0
-* Adds disbursement details to transactions.
-* Adds image_url to transactions.
-
-## 2.20.0
-
-* Support requests >= 1.0
-* Add new validation errors and rename old ones
-
-## 2.19.0
-
-* Adds channel field to transactions.
-
-## 2.18.0
-
-* Add additional card types for card type indicators
-
-## 2.17.0
-
-* Adds verification search
-
-## 2.16.0
-
-* Additional card information, such as prepaid, debit, commercial, Durbin regulated, healthcare, and payroll, are returned on credit card responses
-* Allows transactions to be specified as recurring
-
-## 2.15.0
-
-* Adds prepaid attribute to credit cards (possible values of: Yes, No, Unknown)
-
-## 2.14.2
-
-* Add settling transaction status to transaction search
-
-## 2.14.1
-
-* Adds new package braintree.util.http_stategy to setup.py
-
-## 2.14.0
-
-* Removes relative imports for python 3.0 (thanks [MichaelBlume](https://github.com/MichaelBlume))
-* Adds webhook gateways for parsing, verifying, and testing incoming notifications
-* Allow specifying the http strategy to use (PycURL, httplib, requests)
-
-## 2.13.0
-
-* Adds search for duplicate credit cards given a payment method token
-* Adds flag to fail saving credit card to vault if card is duplicate
-
-## 2.12.3
-
-* Exposes plan_id on transactions
-
-## 2.12.2
-
-* Added error code for invalid purchase order number
-* Fixed zip_safe=False error when building (GitHub issue #17)
-
-## 2.12.1
-
-* Added error message for merchant accounts that do not support refunds
-
-## 2.12.0
-
-* Added ability to retrieve all Plans, AddOns, and Discounts
-* Added Transaction cloning
-
-## 2.11.0
-
-* Added SettlementBatchSummary
-
-## 2.10.1
-
-* Enabled gzip encoding for HTTP requests
-* Fixed handling of long integers when generating xml (thanks [glencoates](https://github.com/glencoates))
-* Added new error code
-
-## 2.10.0
-
-* Added subscription_details to Transaction
-* Added flag to store in vault only when a transaction is successful
-* Added new error code
-
-## 2.9.1
-
-* Added improvements to unicode handling.
-
-## 2.9.0
-
-* Added a new transaction state, AuthorizationExpired.
-* Enabled searching by authorization_expired_at.
-
-## 2.8.0
-
-* Added next_billing_date and transaction_id to subscription search
-* Added address_country_name to customer search
-* Added new error codes
-
-## 2.7.0
-
-* Added Customer search
-* Added dynamic descriptors to Subscriptions and Transactions
-* Added level 2 fields to Transactions:
-  * tax_amount
-  * tax_exempt
-  * purchase_order_number
-
-## 2.6.1
-
-* Added billing_address_id to allowed parameters for credit cards create and update
-* Allow searching on subscriptions that are currently in a trial period using in_trial_period
-
-## 2.6.0
-
-* Added ability to perform multiple partial refunds on Transactions
-* Deprecated Transaction refund_id in favor of refund_ids
-* Added revert_subscription_on_proration_failure flag to Subscription update that specifies how a Subscription should react to a failed proration charge
-* Deprecated Subscription next_bill_amount in favor of next_billing_period_amount
-* Added pycurl dependency in place of M2Crypto for better cross-platform compatibility
-* Added new fields to Subscription:
-  * balance
-  * paid_through_date
-  * next_billing_period_amount
-
-## 2.5.0
-
-* Added AddOns/Discounts
-* Enhanced Subscription search
-* Enhanced Transaction search
-* Added constants for CreditCardVerification statuses
-* Added Expired and Pending statuses to Subscription
-* Allowed prorate_charges to be specified on Subscription update
-* Allowed argument lists and literal lists when searching for Subscriptions and Transactions
-* Added AddOn/Discount details to Transactions that were created from a Subscription
-* All Braintree exceptions now inherit from BraintreeError superclass
-* Removed 13 digit Visa Sandbox Credit Card number and replaced it with a 16 digit Visa
-* Made gateway operations threadsafe when using multiple configurations
-* Added new fields to Subscription:
-  * billing_day_of_month
-  * days_past_due
-  * first_billing_date
-  * never_expires
-  * number_of_billing_cycles
-
-## 2.4.1
-
-* Added support for M2Crypto version 0.20.1, which is the default for Ubuntu Lucid (thanks [foresto](https://github.com/foresto))
-
-## 2.4.0
-
-* Added unified message to ErrorResult
-* Added ability to specify country using country_name, country_code_alpha2, country_code_alpha3, or country_code_numeric (see [ISO_3166-1](https://en.wikipedia.org/wiki/ISO_3166-1))
-* Renamed Subscription retryCharge to retry_charge
-* Added gateway_rejection_reason to Transaction and Verification
-* Allow searching with date objects (in addition to datetime)
-* When creating a Subscription, return failed transaction on the ErrorResult if the initial transaction is not successful
-
-## 2.3.0
-
-* Added unified TransparentRedirect url and confirm methods and deprecated old methods
-* Added methods to CreditCard to allow searching on expiring and expired credit cards
-* Allow credit card verification against a specified merchant account
-* Added all method on Customer to retrieve all customers
-* Added ability to update a customer, credit card, and billing address in one request
-* Allow updating the payment method token on a subscription
-* Added methods to navigate between a Transaction and its refund (in both directions)
-
-## 2.2.1
-
-* Use isinstance instead of type to cater to inheritance (thanks [danielgtaylor](https://github.com/danielgtaylor))
-
-## 2.2.0
-
-* Prevent race condition when pulling back collection results -- search results represent the state of the data at the time the query was run
-* Rename ResourceCollection's approximate_size to maximum_size because items that no longer match the query will not be returned in the result set
-* Correctly handle HTTP error 426 (Upgrade Required) -- the error code is returned when your client library version is no longer compatible with the gateway
-
-## 2.1.0
-
-* Added transaction advanced search
-* Added ability to partially refund transactions
-* Added ability to manually retry past-due subscriptions
-* Added new transaction error codes
-* Allow merchant account to be specified when creating transactions
-* Allow creating a transaction with a vault customer and new credit card
-* Allow existing billing address to be updated when updating credit card
-
-## 2.0.0
-
-* Updated is_success on transaction results to return false on declined transactions
-* Search results now return a generator and will automatically paginate data
-* Allow passing cardholder_name when creating transactions
-
-## 1.2.0
-
-* Renamed ValidationErrorCollection#all to deep_errors and made it a property
-* Added the ability to make a credit card the default card for a customer
-* Updated Quick Start in README.md to show a workflow with error checking
-
-## 1.1.0
-
-* Added subscription search
-* Return associated subscriptions when finding credit cards
-* Raise down for maintenance error instead of forged query string error on 503 responses
-* Updated SSL CA file
-
-## 1.0.0
-
-* Initial release
diff --git a/Dockerfile b/Dockerfile
deleted file mode 100644
index 1211b35..0000000
--- a/Dockerfile
+++ /dev/null
@@ -1,9 +0,0 @@
-FROM debian:stretch
-
-RUN apt-get update
-RUN apt-get -y install python rake
-RUN apt-get -y install python-pip
-RUN pip install --upgrade pip
-RUN pip install --upgrade distribute
-
-WORKDIR /braintree-python
diff --git a/Makefile b/Makefile
deleted file mode 100644
index f4cbed5..0000000
--- a/Makefile
+++ /dev/null
@@ -1,7 +0,0 @@
-.PHONY: console build
-
-console: build
-	docker run -it -v="$(PWD):/braintree-python" --net="host" braintree-python /bin/bash -l -c "pip install -r requirements.txt;bash"
-
-build:
-	docker build -t braintree-python .
diff --git a/PKG-INFO b/PKG-INFO
new file mode 100644
index 0000000..52d84bd
--- /dev/null
+++ b/PKG-INFO
@@ -0,0 +1,21 @@
+Metadata-Version: 2.1
+Name: braintree
+Version: 4.18.0
+Summary: Braintree Python Library
+Home-page: https://developer.paypal.com/braintree/docs/reference/overview
+Author: Braintree
+Author-email: support@braintreepayments.com
+License: MIT
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
+License-File: LICENSE
+
+
+        The Braintree Python SDK provides integration access to the Braintree Gateway.
+
+        1. https://github.com/braintree/braintree_python - README and Samples
+        2. https://developer.paypal.com/braintree/docs/reference/overview - API Reference
+      
diff --git a/README.md b/README.md
index 67b96a0..627480b 100644
--- a/README.md
+++ b/README.md
@@ -2,70 +2,66 @@
 
 The Braintree Python library provides integration access to the Braintree Gateway.
 
-## Please Note
-> **The Payment Card Industry (PCI) Council has [mandated](https://blog.pcisecuritystandards.org/migrating-from-ssl-and-early-tls) that early versions of TLS be retired from service.  All organizations that handle credit card information are required to comply with this standard. As part of this obligation, Braintree is updating its services to require TLS 1.2 for all HTTPS connections. Braintree will also require HTTP/1.1 for all connections. Please see our [technical documentation](https://github.com/paypal/tls-update) for more information.**
+## TLS 1.2 required
+> **The Payment Card Industry (PCI) Council has [mandated](https://blog.pcisecuritystandards.org/migrating-from-ssl-and-early-tls) that early versions of TLS be retired from service.  All organizations that handle credit card information are required to comply with this standard. As part of this obligation, Braintree has updated its services to require TLS 1.2 for all HTTPS connections. Braintrees require HTTP/1.1 for all connections. Please see our [technical documentation](https://github.com/paypal/tls-update) for more information.**
 
 ## Dependencies
 
 * [requests](http://docs.python-requests.org/en/latest/)
 
-The Braintree Python SDK is tested against Python versions 2.7.9, 3.3.5 and 3.7.0.
+The Braintree Python SDK is tested against Python versions 3.5.3 and 3.8.0.
 
-## Upgrading from 2.x.x to 3.x.x
+_The Python core development community has released [End-of-Life branches](https://devguide.python.org/devcycle/#end-of-life-branches) for Python versions 2.7 - 3.4, and are no longer receiving [security updates](https://devguide.python.org/#branchstatus). As a result, Braintree no longer supports these versions of Python._
 
-On Python 2.6 or 2.7 with default settings / requests:
+## Versions
 
-No changes are required to upgrade to version 3.
+Braintree employs a deprecation policy for our SDKs. For more information on the statuses of an SDK check our [developer docs](https://developer.paypal.com/braintree/docs/reference/general/server-sdk-deprecation-policy).
 
-On Python 2.6 or 2.7 with pycurl, httplib, or use_unsafe_ssl = True:
-
-Install requests and test that you are able to connect to the Sandbox
-environment with version 3 and without specifying an HTTP strategy.
-The use_unsafe_ssl parameter will be ignored.
-
-On Python 2.5:
-
-Python 2.5 isn't supported by version 3 of the library.
-Most code that runs on 2.5 will work unmodified on Python 2.6.
-After making sure your code works on Python 2.6, follow the
-instructions above for upgrading from pycurl / httplib to requests.
+| Major version number | Status | Released | Deprecated | Unsupported |
+| -------------------- | ------ | -------- | ---------- | ----------- |
+| 4.x.x | Active | March 2020 | TBA | TBA |
+| 3.x.x | Inactive | June 2014 | March 2022 | March 2023 |
 
 ## Documentation
 
- * [Official documentation](https://developers.braintreepayments.com/ios+python/start/hello-server)
+ * [Official documentation](https://developer.paypal.com/braintree/docs/start/hello-server/python)
+
+Updating from an Inactive, Deprecated, or Unsupported version of this SDK? Check our [Migration Guide](https://developer.paypal.com/braintree/docs/reference/general/server-sdk-migration-guide/python) for tips.
 
 ## Quick Start Example
 
-    import braintree
+```python
+import braintree
 
-    gateway = braintree.BraintreeGateway(
-        braintree.Configuration(
-            environment=braintree.Environment.Sandbox
-            merchant_id="your_merchant_id",
-            public_key="your_public_key",
-            private_key="your_private_key",
-        )
+gateway = braintree.BraintreeGateway(
+    braintree.Configuration(
+        environment=braintree.Environment.Sandbox
+        merchant_id="your_merchant_id",
+        public_key="your_public_key",
+        private_key="your_private_key",
     )
-
-    result = gateway.transaction.sale({
-        "amount": "1000.00",
-        "payment_method_nonce": nonce_from_the_client,
-        "options": {
-            "submit_for_settlement": True
-        }
-    })
-
-    if result.is_success:
-        print("success!: " + result.transaction.id)
-    elif result.transaction:
-        print("Error processing transaction:")
-        print("  code: " + result.transaction.processor_response_code)
-        print("  text: " + result.transaction.processor_response_text)
-    else:
-        for error in result.errors.deep_errors:
-            print("attribute: " + error.attribute)
-            print("  code: " + error.code)
-            print("  message: " + error.message)
+)
+
+result = gateway.transaction.sale({
+    "amount": "1000.00",
+    "payment_method_nonce": nonce_from_the_client,
+    "options": {
+        "submit_for_settlement": True
+    }
+})
+
+if result.is_success:
+    print("success!: " + result.transaction.id)
+elif result.transaction:
+    print("Error processing transaction:")
+    print("  code: " + result.transaction.processor_response_code)
+    print("  text: " + result.transaction.processor_response_text)
+else:
+    for error in result.errors.deep_errors:
+        print("attribute: " + error.attribute)
+        print("  code: " + error.code)
+        print("  message: " + error.message)
+```
 
 ## Developing
 
@@ -84,7 +80,7 @@ instructions above for upgrading from pycurl / httplib to requests.
 3. Install dependencies:
 
    ```
-   pip install -r requirements.txt
+   pip3 install -r dev_requirements.txt
    ```
 
 ## Developing (Docker)
@@ -99,7 +95,7 @@ make
 
 Our friends at [Venmo](https://venmo.com) have [an open source library](https://github.com/venmo/btnamespace) designed to simplify testing of applications using this library.
 
-If you wish to run the tests, make sure you are set up for development (see instructions above). The unit specs can be run by anyone on any system, but the integration specs are meant to be run against a local development server of our gateway code. These integration specs are not meant for public consumption and will likely fail if run on your system. To run unit tests use rake (`rake test:unit`) or nose (`nosetests tests/unit`).
+If you wish to run the tests, make sure you are set up for development (see instructions above). The unit specs can be run by anyone on any system, but the integration specs are meant to be run against a local development server of our gateway code. These integration specs are not meant for public consumption and will likely fail if run on your system. To run unit tests use rake (`rake test:unit`) or unittest (`python3 -m unittest discover tests/unit`).
 
 ## License
 
diff --git a/Rakefile b/Rakefile
deleted file mode 100644
index e45b219..0000000
--- a/Rakefile
+++ /dev/null
@@ -1,58 +0,0 @@
-task :default => :test
-
-task :test => ["test:unit", "test:integration"]
-
-namespace :test do
-  desc "run unit tests"
-  task :unit do
-    sh "nosetests tests/unit"
-  end
-
-  desc "run integration tests"
-  task :integration do
-    sh "env nosetests tests/integration"
-  end
-
-  desc "run single test (example: rake test:single[tests/integration/test_paypal_account.py:TestPayPalAccount.test_find_returns_paypal_account])"
-  task :single, [:test_name] do |t, args|
-      sh "nosetests #{args[:test_name]}"
-  end
-end
-
-task :clean do
-  rm_rf "build"
-  rm_rf "dist"
-  rm_f "MANIFEST"
-end
-
-namespace :pypi do
-  desc "Register the package with PyPI"
-  task :register => :clean do
-    sh "python setup.py register"
-  end
-
-  desc "Upload a new version to PyPI"
-  task :upload => :clean do
-    sh "python setup.py sdist bdist_wheel"
-    sh "twine upload dist/*"
-  end
-end
-
-namespace :lint do
-  desc "Evaluate test code quality using pylintrc file"
-  task :tests do
-    puts `pylint tests --rcfile=.pylintrc --disable=R0801 --disable=W0232`
-  end
-
-  desc "Evaluate app code quality using pylintrc file"
-  task :code do
-    puts `pylint braintree --rcfile=.pylintrc`
-  end
-
-  desc "Evaluate library code quality using pylintrc file"
-  task :all do
-    puts `pylint braintree tests --rcfile=.pylintrc`
-  end
-end
-
-task :lint => "lint:all"
diff --git a/braintree.egg-info/PKG-INFO b/braintree.egg-info/PKG-INFO
new file mode 100644
index 0000000..52d84bd
--- /dev/null
+++ b/braintree.egg-info/PKG-INFO
@@ -0,0 +1,21 @@
+Metadata-Version: 2.1
+Name: braintree
+Version: 4.18.0
+Summary: Braintree Python Library
+Home-page: https://developer.paypal.com/braintree/docs/reference/overview
+Author: Braintree
+Author-email: support@braintreepayments.com
+License: MIT
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
+License-File: LICENSE
+
+
+        The Braintree Python SDK provides integration access to the Braintree Gateway.
+
+        1. https://github.com/braintree/braintree_python - README and Samples
+        2. https://developer.paypal.com/braintree/docs/reference/overview - API Reference
+      
diff --git a/braintree.egg-info/SOURCES.txt b/braintree.egg-info/SOURCES.txt
new file mode 100644
index 0000000..fd471a9
--- /dev/null
+++ b/braintree.egg-info/SOURCES.txt
@@ -0,0 +1,189 @@
+LICENSE
+MANIFEST.in
+README.md
+setup.cfg
+setup.py
+braintree/__init__.py
+braintree/account_updater_daily_report.py
+braintree/ach_mandate.py
+braintree/add_on.py
+braintree/add_on_gateway.py
+braintree/address.py
+braintree/address_gateway.py
+braintree/amex_express_checkout_card.py
+braintree/android_pay_card.py
+braintree/apple_pay_card.py
+braintree/apple_pay_gateway.py
+braintree/apple_pay_options.py
+braintree/attribute_getter.py
+braintree/authorization_adjustment.py
+braintree/bin_data.py
+braintree/braintree_gateway.py
+braintree/client_token.py
+braintree/client_token_gateway.py
+braintree/configuration.py
+braintree/connected_merchant_paypal_status_changed.py
+braintree/connected_merchant_status_transitioned.py
+braintree/credentials_parser.py
+braintree/credit_card.py
+braintree/credit_card_gateway.py
+braintree/credit_card_verification.py
+braintree/credit_card_verification_gateway.py
+braintree/credit_card_verification_search.py
+braintree/customer.py
+braintree/customer_gateway.py
+braintree/customer_search.py
+braintree/descriptor.py
+braintree/disbursement.py
+braintree/disbursement_detail.py
+braintree/discount.py
+braintree/discount_gateway.py
+braintree/dispute.py
+braintree/dispute_gateway.py
+braintree/dispute_search.py
+braintree/document_upload.py
+braintree/document_upload_gateway.py
+braintree/enriched_customer_data.py
+braintree/environment.py
+braintree/error_codes.py
+braintree/error_result.py
+braintree/errors.py
+braintree/europe_bank_account.py
+braintree/exchange_rate_quote.py
+braintree/exchange_rate_quote_gateway.py
+braintree/exchange_rate_quote_input.py
+braintree/exchange_rate_quote_payload.py
+braintree/exchange_rate_quote_request.py
+braintree/facilitated_details.py
+braintree/facilitator_details.py
+braintree/granted_payment_instrument_update.py
+braintree/iban_bank_account.py
+braintree/ids_search.py
+braintree/liability_shift.py
+braintree/local_payment.py
+braintree/local_payment_completed.py
+braintree/local_payment_expired.py
+braintree/local_payment_funded.py
+braintree/local_payment_reversed.py
+braintree/masterpass_card.py
+braintree/merchant.py
+braintree/merchant_account_gateway.py
+braintree/merchant_gateway.py
+braintree/modification.py
+braintree/montary_amount.py
+braintree/oauth_access_revocation.py
+braintree/oauth_credentials.py
+braintree/oauth_gateway.py
+braintree/paginated_collection.py
+braintree/paginated_result.py
+braintree/partner_merchant.py
+braintree/payment_instrument_type.py
+braintree/payment_method.py
+braintree/payment_method_customer_data_updated_metadata.py
+braintree/payment_method_gateway.py
+braintree/payment_method_nonce.py
+braintree/payment_method_nonce_gateway.py
+braintree/payment_method_parser.py
+braintree/paypal_account.py
+braintree/paypal_account_gateway.py
+braintree/paypal_here.py
+braintree/plan.py
+braintree/plan_gateway.py
+braintree/processor_response_types.py
+braintree/resource.py
+braintree/resource_collection.py
+braintree/revoked_payment_method_metadata.py
+braintree/risk_data.py
+braintree/samsung_pay_card.py
+braintree/search.py
+braintree/sepa_direct_debit_account.py
+braintree/sepa_direct_debit_account_gateway.py
+braintree/settlement_batch_summary.py
+braintree/settlement_batch_summary_gateway.py
+braintree/signature_service.py
+braintree/status_event.py
+braintree/subscription.py
+braintree/subscription_details.py
+braintree/subscription_gateway.py
+braintree/subscription_search.py
+braintree/subscription_status_event.py
+braintree/successful_result.py
+braintree/testing_gateway.py
+braintree/three_d_secure_info.py
+braintree/transaction.py
+braintree/transaction_amounts.py
+braintree/transaction_details.py
+braintree/transaction_gateway.py
+braintree/transaction_line_item.py
+braintree/transaction_line_item_gateway.py
+braintree/transaction_review.py
+braintree/transaction_search.py
+braintree/unknown_payment_method.py
+braintree/us_bank_account.py
+braintree/us_bank_account_gateway.py
+braintree/us_bank_account_verification.py
+braintree/us_bank_account_verification_gateway.py
+braintree/us_bank_account_verification_search.py
+braintree/validation_error.py
+braintree/validation_error_collection.py
+braintree/venmo_account.py
+braintree/venmo_profile_data.py
+braintree/version.py
+braintree/visa_checkout_card.py
+braintree/webhook_notification.py
+braintree/webhook_notification_gateway.py
+braintree/webhook_testing.py
+braintree/webhook_testing_gateway.py
+braintree.egg-info/PKG-INFO
+braintree.egg-info/SOURCES.txt
+braintree.egg-info/dependency_links.txt
+braintree.egg-info/not-zip-safe
+braintree.egg-info/requires.txt
+braintree.egg-info/top_level.txt
+braintree/dispute_details/__init__.py
+braintree/dispute_details/evidence.py
+braintree/dispute_details/paypal_message.py
+braintree/dispute_details/status_history.py
+braintree/exceptions/__init__.py
+braintree/exceptions/authentication_error.py
+braintree/exceptions/authorization_error.py
+braintree/exceptions/braintree_error.py
+braintree/exceptions/configuration_error.py
+braintree/exceptions/gateway_timeout_error.py
+braintree/exceptions/invalid_challenge_error.py
+braintree/exceptions/invalid_signature_error.py
+braintree/exceptions/not_found_error.py
+braintree/exceptions/request_timeout_error.py
+braintree/exceptions/server_error.py
+braintree/exceptions/service_unavailable_error.py
+braintree/exceptions/test_operation_performed_in_production_error.py
+braintree/exceptions/too_many_requests_error.py
+braintree/exceptions/unexpected_error.py
+braintree/exceptions/upgrade_required_error.py
+braintree/exceptions/http/__init__.py
+braintree/exceptions/http/connection_error.py
+braintree/exceptions/http/invalid_response_error.py
+braintree/exceptions/http/timeout_error.py
+braintree/merchant_account/__init__.py
+braintree/merchant_account/address_details.py
+braintree/merchant_account/business_details.py
+braintree/merchant_account/funding_details.py
+braintree/merchant_account/individual_details.py
+braintree/merchant_account/merchant_account.py
+braintree/ssl/api_braintreegateway_com.ca.crt
+braintree/test/__init__.py
+braintree/test/authentication_ids.py
+braintree/test/credit_card_defaults.py
+braintree/test/credit_card_numbers.py
+braintree/test/merchant_account.py
+braintree/test/nonces.py
+braintree/test/venmo_sdk.py
+braintree/util/__init__.py
+braintree/util/constants.py
+braintree/util/crypto.py
+braintree/util/datetime_parser.py
+braintree/util/generator.py
+braintree/util/graphql_client.py
+braintree/util/http.py
+braintree/util/parser.py
+braintree/util/xml_util.py
\ No newline at end of file
diff --git a/braintree.egg-info/dependency_links.txt b/braintree.egg-info/dependency_links.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/braintree.egg-info/dependency_links.txt
@@ -0,0 +1 @@
+
diff --git a/braintree.egg-info/not-zip-safe b/braintree.egg-info/not-zip-safe
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/braintree.egg-info/not-zip-safe
@@ -0,0 +1 @@
+
diff --git a/braintree.egg-info/requires.txt b/braintree.egg-info/requires.txt
new file mode 100644
index 0000000..ea6f246
--- /dev/null
+++ b/braintree.egg-info/requires.txt
@@ -0,0 +1 @@
+requests<3.0,>=0.11.1
diff --git a/braintree.egg-info/top_level.txt b/braintree.egg-info/top_level.txt
new file mode 100644
index 0000000..28e3282
--- /dev/null
+++ b/braintree.egg-info/top_level.txt
@@ -0,0 +1 @@
+braintree
diff --git a/braintree/__init__.py b/braintree/__init__.py
index 47188bf..c30d401 100644
--- a/braintree/__init__.py
+++ b/braintree/__init__.py
@@ -6,11 +6,12 @@ from braintree.address_gateway import AddressGateway
 from braintree.amex_express_checkout_card import AmexExpressCheckoutCard
 from braintree.android_pay_card import AndroidPayCard
 from braintree.apple_pay_card import ApplePayCard
+from braintree.apple_pay_gateway import ApplePayGateway
 from braintree.braintree_gateway import BraintreeGateway
 from braintree.client_token import ClientToken
 from braintree.configuration import Configuration
-from braintree.connected_merchant_status_transitioned import ConnectedMerchantStatusTransitioned
 from braintree.connected_merchant_paypal_status_changed import ConnectedMerchantPayPalStatusChanged
+from braintree.connected_merchant_status_transitioned import ConnectedMerchantStatusTransitioned
 from braintree.credentials_parser import CredentialsParser
 from braintree.credit_card import CreditCard
 from braintree.credit_card_gateway import CreditCardGateway
@@ -21,19 +22,21 @@ from braintree.customer_gateway import CustomerGateway
 from braintree.customer_search import CustomerSearch
 from braintree.descriptor import Descriptor
 from braintree.disbursement import Disbursement
-from braintree.document_upload import DocumentUpload
-from braintree.document_upload_gateway import DocumentUploadGateway
 from braintree.discount import Discount
 from braintree.discount_gateway import DiscountGateway
 from braintree.dispute import Dispute
 from braintree.dispute_search import DisputeSearch
+from braintree.document_upload import DocumentUpload
+from braintree.document_upload_gateway import DocumentUploadGateway
+from braintree.enriched_customer_data import EnrichedCustomerData
 from braintree.environment import Environment
 from braintree.error_codes import ErrorCodes
 from braintree.error_result import ErrorResult
 from braintree.errors import Errors
 from braintree.europe_bank_account import EuropeBankAccount
-from braintree.us_bank_account import UsBankAccount
-from braintree.ideal_payment import IdealPayment
+from braintree.liability_shift import LiabilityShift
+from braintree.local_payment_completed import LocalPaymentCompleted
+from braintree.local_payment_reversed import LocalPaymentReversed
 from braintree.merchant import Merchant
 from braintree.merchant_account import MerchantAccount
 from braintree.merchant_account_gateway import MerchantAccountGateway
@@ -41,6 +44,7 @@ from braintree.oauth_access_revocation import OAuthAccessRevocation
 from braintree.partner_merchant import PartnerMerchant
 from braintree.payment_instrument_type import PaymentInstrumentType
 from braintree.payment_method import PaymentMethod
+from braintree.payment_method_customer_data_updated_metadata import PaymentMethodCustomerDataUpdatedMetadata
 from braintree.payment_method_nonce import PaymentMethodNonce
 from braintree.payment_method_parser import parse_payment_method
 from braintree.paypal_account import PayPalAccount
@@ -51,6 +55,7 @@ from braintree.resource_collection import ResourceCollection
 from braintree.risk_data import RiskData
 from braintree.samsung_pay_card import SamsungPayCard
 from braintree.search import Search
+from braintree.sepa_direct_debit_account import SepaDirectDebitAccount
 from braintree.settlement_batch_summary import SettlementBatchSummary
 from braintree.signature_service import SignatureService
 from braintree.status_event import StatusEvent
@@ -67,11 +72,11 @@ from braintree.transaction_details import TransactionDetails
 from braintree.transaction_gateway import TransactionGateway
 from braintree.transaction_line_item import TransactionLineItem
 from braintree.transaction_search import TransactionSearch
-from braintree.transparent_redirect import TransparentRedirect
-from braintree.transparent_redirect_gateway import TransparentRedirectGateway
 from braintree.unknown_payment_method import UnknownPaymentMethod
+from braintree.us_bank_account import UsBankAccount
 from braintree.validation_error_collection import ValidationErrorCollection
 from braintree.venmo_account import VenmoAccount
+from braintree.venmo_profile_data import VenmoProfileData
 from braintree.version import Version
 from braintree.webhook_notification import WebhookNotification
 from braintree.webhook_notification_gateway import WebhookNotificationGateway
diff --git a/braintree/address.py b/braintree/address.py
index 5717950..c82b55e 100644
--- a/braintree/address.py
+++ b/braintree/address.py
@@ -42,12 +42,31 @@ class Address(Resource):
             "postal_code",
             "region",
             "street_address",
+            "shipping_method",
         ]
         return super(Address, self).__repr__(detail_list)
 
+    # NEXT_MAJOR_VERSION this can be an enum! they were added as of python 3.4 and we support 3.5+
+    class ShippingMethod(object):
+        """
+        Constants representing shipping methods for shipping addresses. Available types are:
+
+        * braintree.Address.ShippingMethod.SameDay
+        * braintree.Address.ShippingMethod.NextDay
+        * braintree.Address.ShippingMethod.Priority
+        * braintree.Address.ShippingMethod.Ground
+        * braintree.Address.ShippingMethod.Electronic
+        * braintree.Address.ShippingMethod.ShipToStore
+        """
+        SameDay     = "same_day"
+        NextDay     = "next_day"
+        Priority    = "priority"
+        Ground      = "ground"
+        Electronic  = "electronic"
+        ShipToStore = "ship_to_store"
 
     @staticmethod
-    def create(params={}):
+    def create(params=None):
         """
         Create an Address.
 
@@ -61,7 +80,8 @@ class Address(Resource):
             })
 
         """
-
+        if params is None:
+            params = {}
         return Configuration.gateway().address.create(params)
 
     @staticmethod
@@ -89,7 +109,7 @@ class Address(Resource):
         return Configuration.gateway().address.find(customer_id, address_id)
 
     @staticmethod
-    def update(customer_id, address_id, params={}):
+    def update(customer_id, address_id, params=None):
         """
         Update an existing Address.
 
@@ -100,14 +120,15 @@ class Address(Resource):
             })
 
         """
-
+        if params is None:
+            params = {}
         return Configuration.gateway().address.update(customer_id, address_id, params)
 
     @staticmethod
     def create_signature():
         return ["company", "country_code_alpha2", "country_code_alpha3", "country_code_numeric",
                 "country_name", "customer_id", "extended_address", "first_name",
-                "last_name", "locality", "postal_code", "region", "street_address"]
+                "last_name", "locality", "phone_number", "postal_code", "region", "street_address"]
 
     @staticmethod
     def update_signature():
diff --git a/braintree/address_gateway.py b/braintree/address_gateway.py
index 99fc90f..7eeabdf 100644
--- a/braintree/address_gateway.py
+++ b/braintree/address_gateway.py
@@ -11,11 +11,19 @@ class AddressGateway(object):
         self.gateway = gateway
         self.config = gateway.config
 
-    def create(self, params={}):
+    def __validate_chars_in_args(self, customer_id, address_id):
+        if not re.search(r"\A[0-9A-Za-z_-]+\Z", customer_id):
+            raise KeyError("customer_id contains invalid characters")
+        if not re.search(r"\A[0-9A-Za-z]+\Z", address_id):
+            raise KeyError("address_id contains invalid characters")
+
+    def create(self, params=None):
+        if params is None:
+            params = {}
         Resource.verify_keys(params, Address.create_signature())
-        if not "customer_id" in params:
+        if "customer_id" not in params:
             raise KeyError("customer_id must be provided")
-        if not re.search("\A[0-9A-Za-z_-]+\Z", params["customer_id"]):
+        if not re.search(r"\A[0-9A-Za-z_-]+\Z", params["customer_id"]):
             raise KeyError("customer_id contains invalid characters")
 
         response = self.config.http().post(self.config.base_merchant_path() + "/customers/" + params.pop("customer_id") + "/addresses", {"address": params})
@@ -25,20 +33,26 @@ class AddressGateway(object):
             return ErrorResult(self.gateway, response["api_error_response"])
 
     def delete(self, customer_id, address_id):
+        self.__validate_chars_in_args(customer_id, address_id)
         self.config.http().delete(self.config.base_merchant_path() + "/customers/" + customer_id + "/addresses/" + address_id)
         return SuccessfulResult()
 
     def find(self, customer_id, address_id):
         try:
+            # NEXT_MAJOR_VERSION return KeyError instead of NotFoundError, it's a more helpful error message to the developer that way
             if customer_id is None or customer_id.strip() == "" or address_id is None or address_id.strip() == "":
                 raise NotFoundError()
+            self.__validate_chars_in_args(customer_id, address_id)
             response = self.config.http().get(self.config.base_merchant_path() + "/customers/" + customer_id + "/addresses/" + address_id)
             return Address(self.gateway, response["address"])
         except NotFoundError:
             raise NotFoundError("address for customer " + repr(customer_id) + " with id " + repr(address_id) + " not found")
 
-    def update(self, customer_id, address_id, params={}):
+    def update(self, customer_id, address_id, params=None):
+        if params is None:
+            params = {}
         Resource.verify_keys(params, Address.update_signature())
+        self.__validate_chars_in_args(customer_id, address_id)
         response = self.config.http().put(
             self.config.base_merchant_path() + "/customers/" + customer_id + "/addresses/" + address_id,
             {"address": params}
diff --git a/braintree/amex_express_checkout_card.py b/braintree/amex_express_checkout_card.py
index 42af24d..3cf8f2c 100644
--- a/braintree/amex_express_checkout_card.py
+++ b/braintree/amex_express_checkout_card.py
@@ -1,11 +1,13 @@
 import braintree
 from braintree.resource import Resource
+from warnings import warn
 
 class AmexExpressCheckoutCard(Resource):
     """
-    A class representing Braintree Amex Express Checkout card objects.
+    A class representing Braintree Amex Express Checkout card objects. Deprecated
     """
     def __init__(self, gateway, attributes):
+        warn("AmexExpressCheckoutCard is deprecated")
         Resource.__init__(self, gateway, attributes)
 
         if "subscriptions" in attributes:
@@ -14,4 +16,3 @@ class AmexExpressCheckoutCard(Resource):
     @property
     def expiration_date(self):
         return self.expiration_month + "/" + self.expiration_year
-
diff --git a/braintree/android_pay_card.py b/braintree/android_pay_card.py
index 33c56f0..a8f27de 100644
--- a/braintree/android_pay_card.py
+++ b/braintree/android_pay_card.py
@@ -1,6 +1,7 @@
 import braintree
 from braintree.resource import Resource
 
+# NEXT_MAJOR_VERSION - rename to GooglePayCard
 class AndroidPayCard(Resource):
     """
     A class representing Braintree Android Pay card objects.
diff --git a/braintree/apple_pay_card.py b/braintree/apple_pay_card.py
index 0c16fa0..ce00c6a 100644
--- a/braintree/apple_pay_card.py
+++ b/braintree/apple_pay_card.py
@@ -30,3 +30,40 @@ class ApplePayCard(Resource):
     def expiration_date(self):
         return self.expiration_month + "/" + self.expiration_year
 
+    @staticmethod
+    def signature():
+        options = ["make_default"]
+
+        signature = [
+            "customer_id",
+            "cardholder_name",
+            "expiration_month",
+            "expiration_year",
+            "number",
+            "cryptogram",
+            "eci_indicator",
+            "token",
+            {
+                "options": options
+            },
+            {
+                "billing_address": [
+                    "company",
+                    "country_code_alpha2",
+                    "country_code_alpha3",
+                    "country_code_numeric",
+                    "country_name",
+                    "extended_address",
+                    "first_name",
+                    "last_name",
+                    "locality",
+                    "postal_code",
+                    "phone_number",
+                    "region",
+                    "street_address"
+                ]
+            }
+        ]
+
+        return signature
+
diff --git a/braintree/apple_pay_gateway.py b/braintree/apple_pay_gateway.py
new file mode 100644
index 0000000..bb5cea6
--- /dev/null
+++ b/braintree/apple_pay_gateway.py
@@ -0,0 +1,34 @@
+try:
+    from html import escape
+except ImportError:
+    from cgi import escape
+
+from braintree.apple_pay_options import ApplePayOptions
+from braintree.error_result import ErrorResult
+from braintree.successful_result import SuccessfulResult
+from braintree.exceptions.unexpected_error import UnexpectedError
+
+class ApplePayGateway(object):
+    def __init__(self, gateway):
+        self.gateway = gateway
+        self.config = gateway.config
+
+    def register_domain(self, domain):
+        response = self.config.http().post(self.config.base_merchant_path() + "/processing/apple_pay/validate_domains", {'url': domain})
+
+        if "response" in response and response["response"]["success"]:
+            return SuccessfulResult()
+        elif response["api_error_response"]:
+            return ErrorResult(self.gateway, response["api_error_response"])
+
+    def unregister_domain(self, domain):
+        self.config.http().delete(self.config.base_merchant_path() + "/processing/apple_pay/unregister_domain?url=" + escape(domain))
+        return SuccessfulResult()
+
+    def registered_domains(self):
+        response = self.config.http().get(self.config.base_merchant_path() + "/processing/apple_pay/registered_domains")
+
+        if "response" in response:
+            response = ApplePayOptions(response.pop("response"))
+
+        return response.domains
diff --git a/braintree/apple_pay_options.py b/braintree/apple_pay_options.py
new file mode 100644
index 0000000..24bf37e
--- /dev/null
+++ b/braintree/apple_pay_options.py
@@ -0,0 +1,4 @@
+from braintree.attribute_getter import AttributeGetter
+
+class ApplePayOptions(AttributeGetter):
+    pass
diff --git a/braintree/attribute_getter.py b/braintree/attribute_getter.py
index a4067c0..43049a8 100644
--- a/braintree/attribute_getter.py
+++ b/braintree/attribute_getter.py
@@ -1,23 +1,28 @@
 class AttributeGetter(object):
     """
-    Helper class for objects that define their attributes from dictionaries 
+    Helper class for objects that define their attributes from dictionaries
     passed in during instantiation.
-    
+
     Example:
-    
+
     a = AttributeGetter({'foo': 'bar', 'baz': 5})
     a.foo
     >> 'bar'
     a.baz
     >> 5
-    
+
     Typically inherited by subclasses instead of directly instantiated.
     """
-    def __init__(self, attributes={}):
+    def __init__(self, attributes=None):
+        if attributes is None:
+            attributes = {}
         self._setattrs = []
         for key, val in attributes.items():
             setattr(self, key, val)
             self._setattrs.append(key)
+            if key == "global_id":
+                setattr(self, "graphql_id", val)
+                self._setattrs.append("graphql_id")
 
     def __repr__(self, detail_list=None):
         if detail_list is None:
diff --git a/braintree/authorization_adjustment.py b/braintree/authorization_adjustment.py
index fafd692..bb395eb 100644
--- a/braintree/authorization_adjustment.py
+++ b/braintree/authorization_adjustment.py
@@ -4,5 +4,5 @@ from braintree.attribute_getter import AttributeGetter
 class AuthorizationAdjustment(AttributeGetter):
     def __init__(self, attributes):
         AttributeGetter.__init__(self, attributes)
-        if hasattr(self, 'amount') and self.amount is not None:
+        if getattr(self, "amount", None) is not None:
             self.amount = Decimal(self.amount)
diff --git a/braintree/braintree_gateway.py b/braintree/braintree_gateway.py
index a0a4619..66744c8 100644
--- a/braintree/braintree_gateway.py
+++ b/braintree/braintree_gateway.py
@@ -1,33 +1,32 @@
 from braintree.add_on_gateway import AddOnGateway
 from braintree.address_gateway import AddressGateway
+from braintree.apple_pay_gateway import ApplePayGateway
 from braintree.client_token_gateway import ClientTokenGateway
 from braintree.configuration import Configuration
 from braintree.credit_card_gateway import CreditCardGateway
 from braintree.credit_card_verification_gateway import CreditCardVerificationGateway
 from braintree.customer_gateway import CustomerGateway
-from braintree.document_upload_gateway import DocumentUploadGateway
 from braintree.discount_gateway import DiscountGateway
 from braintree.dispute_gateway import DisputeGateway
+from braintree.document_upload_gateway import DocumentUploadGateway
+from braintree.exchange_rate_quote_gateway import ExchangeRateQuoteGateway
 from braintree.merchant_account_gateway import MerchantAccountGateway
 from braintree.merchant_gateway import MerchantGateway
 from braintree.oauth_gateway import OAuthGateway
 from braintree.payment_method_gateway import PaymentMethodGateway
 from braintree.payment_method_nonce_gateway import PaymentMethodNonceGateway
 from braintree.paypal_account_gateway import PayPalAccountGateway
+from braintree.sepa_direct_debit_account_gateway import SepaDirectDebitAccountGateway
 from braintree.plan_gateway import PlanGateway
 from braintree.settlement_batch_summary_gateway import SettlementBatchSummaryGateway
 from braintree.subscription_gateway import SubscriptionGateway
 from braintree.testing_gateway import TestingGateway
 from braintree.transaction_gateway import TransactionGateway
 from braintree.transaction_line_item_gateway import TransactionLineItemGateway
-from braintree.transparent_redirect_gateway import TransparentRedirectGateway
 from braintree.us_bank_account_gateway import UsBankAccountGateway
 from braintree.us_bank_account_verification_gateway import UsBankAccountVerificationGateway
 from braintree.webhook_notification_gateway import WebhookNotificationGateway
 from braintree.webhook_testing_gateway import WebhookTestingGateway
-# NEXT_MAJOR_VERSION Remove this class as legacy Ideal has been removed/disabled in the Braintree Gateway
-# DEPRECATED If you're looking to accept iDEAL as a payment method contact accounts@braintreepayments.com for a solution.
-from braintree.ideal_payment_gateway import IdealPaymentGateway
 import braintree.configuration
 
 class BraintreeGateway(object):
@@ -41,33 +40,32 @@ class BraintreeGateway(object):
                 access_token=kwargs.get("access_token"),
                 http_strategy=kwargs.get("http_strategy")
             )
-        self.graphql_client = self.config.graphql_client()
         self.add_on = AddOnGateway(self)
         self.address = AddressGateway(self)
+        self.apple_pay = ApplePayGateway(self)
         self.client_token = ClientTokenGateway(self)
         self.credit_card = CreditCardGateway(self)
         self.customer = CustomerGateway(self)
-        self.document_upload = DocumentUploadGateway(self)
         self.discount = DiscountGateway(self)
         self.dispute = DisputeGateway(self)
-        self.merchant_account = MerchantAccountGateway(self)
+        self.document_upload = DocumentUploadGateway(self)
+        self.exchange_rate_quote = ExchangeRateQuoteGateway(self)
+        self.graphql_client = self.config.graphql_client()
         self.merchant = MerchantGateway(self)
+        self.merchant_account = MerchantAccountGateway(self)
         self.oauth = OAuthGateway(self)
+        self.payment_method = PaymentMethodGateway(self)
+        self.payment_method_nonce = PaymentMethodNonceGateway(self)
+        self.paypal_account = PayPalAccountGateway(self)
         self.plan = PlanGateway(self)
+        self.sepa_direct_debit_account = SepaDirectDebitAccountGateway(self)
         self.settlement_batch_summary = SettlementBatchSummaryGateway(self)
         self.subscription = SubscriptionGateway(self)
+        self.testing = TestingGateway(self)
         self.transaction = TransactionGateway(self)
         self.transaction_line_item = TransactionLineItemGateway(self)
-        self.transparent_redirect = TransparentRedirectGateway(self)
+        self.us_bank_account = UsBankAccountGateway(self)
+        self.us_bank_account_verification = UsBankAccountVerificationGateway(self)
         self.verification = CreditCardVerificationGateway(self)
         self.webhook_notification = WebhookNotificationGateway(self)
         self.webhook_testing = WebhookTestingGateway(self)
-        self.payment_method = PaymentMethodGateway(self)
-        self.payment_method_nonce = PaymentMethodNonceGateway(self)
-        self.paypal_account = PayPalAccountGateway(self)
-        self.testing = TestingGateway(self)
-        self.us_bank_account = UsBankAccountGateway(self)
-        self.us_bank_account_verification = UsBankAccountVerificationGateway(self)
-        # NEXT_MAJOR_VERSION Remove this class as legacy Ideal has been removed/disabled in the Braintree Gateway
-        # DEPRECATED If you're looking to accept iDEAL as a payment method contact accounts@braintreepayments.com for a solution.
-        self.ideal_payment = IdealPaymentGateway(self)
diff --git a/braintree/client_token.py b/braintree/client_token.py
index eb85235..1be04cc 100644
--- a/braintree/client_token.py
+++ b/braintree/client_token.py
@@ -6,10 +6,13 @@ from braintree.signature_service import SignatureService
 from braintree.util.crypto import Crypto
 from braintree import exceptions
 
+
 class ClientToken(object):
 
     @staticmethod
-    def generate(params={}, gateway=None):
+    def generate(params=None, gateway=None):
+        if params is None:
+            params = {}
         if gateway is None:
             gateway = Configuration.gateway().client_token
 
diff --git a/braintree/client_token_gateway.py b/braintree/client_token_gateway.py
index ecea6a2..8273d49 100644
--- a/braintree/client_token_gateway.py
+++ b/braintree/client_token_gateway.py
@@ -3,14 +3,16 @@ from braintree.resource import Resource
 from braintree.client_token import ClientToken
 from braintree import exceptions
 
+
 class ClientTokenGateway(object):
     def __init__(self, gateway):
         self.gateway = gateway
         self.config = gateway.config
 
-
-    def generate(self, params={}):
-        if "options" in params and not "customer_id" in params:
+    def generate(self, params=None):
+        if params is None:
+            params = {}
+        if "options" in params and "customer_id" not in params:
             for option in ["verify_card", "make_default", "fail_on_duplicate_payment_method"]:
                 if option in params["options"]:
                     raise exceptions.InvalidSignatureError("cannot specify %s without a customer_id" % option)
diff --git a/braintree/coinbase_account.py b/braintree/coinbase_account.py
deleted file mode 100644
index 3544cd7..0000000
--- a/braintree/coinbase_account.py
+++ /dev/null
@@ -1,8 +0,0 @@
-import braintree
-from braintree.resource import Resource
-
-class CoinbaseAccount(Resource):
-    def __init__(self, gateway, attributes):
-        Resource.__init__(self, gateway, attributes)
-        if "subscriptions" in attributes:
-            self.subscriptions = [braintree.subscription.Subscription(gateway, subscription) for subscription in self.subscriptions]
diff --git a/braintree/configuration.py b/braintree/configuration.py
index 42fd84a..45bca10 100644
--- a/braintree/configuration.py
+++ b/braintree/configuration.py
@@ -58,7 +58,7 @@ class Configuration(object):
 
     @staticmethod
     def api_version():
-        return "5"
+        return "6"
 
     @staticmethod
     def graphql_api_version():
diff --git a/braintree/credit_card.py b/braintree/credit_card.py
index 3931ef0..7f5a0c5 100644
--- a/braintree/credit_card.py
+++ b/braintree/credit_card.py
@@ -3,9 +3,9 @@ import warnings
 from braintree.resource import Resource
 from braintree.address import Address
 from braintree.configuration import Configuration
-from braintree.transparent_redirect import TransparentRedirect
 from braintree.credit_card_verification import CreditCardVerification
 
+
 class CreditCard(Resource):
     """
     A class representing Braintree CreditCard objects.
@@ -38,7 +38,7 @@ class CreditCard(Resource):
         print(result.credit_card.token)
         print(result.credit_card.masked_number)
 
-    For more information on CreditCards, see https://developers.braintreepayments.com/reference/request/credit-card/create/python
+    For more information on CreditCards, see https://developer.paypal.com/braintree/docs/reference/request/credit-card/create/python
 
     """
     class CardType(object):
@@ -95,6 +95,7 @@ class CreditCard(Resource):
         International = "international"
         US = "us"
 
+    # NEXT_MAJOR_VERSION this can be an enum! they were added as of python 3.4 and we support 3.5+
     class CardTypeIndicator(object):
         """
         Constants representing the three states for the card type indicator attributes
@@ -111,19 +112,7 @@ class CreditCard(Resource):
             CountryOfIssuance = IssuingBank = Payroll = Prepaid = ProductId = CardTypeIndicator
 
     @staticmethod
-    def confirm_transparent_redirect(query_string):
-        """
-        Confirms a transparent redirect request. It expects the query string from the
-        redirect request. The query string should _not_ include the leading "?" character. ::
-
-            result = braintree.CreditCard.confirm_transparent_redirect_request("foo=bar&id=12345")
-        """
-
-        warnings.warn("Please use TransparentRedirect.confirm instead", DeprecationWarning)
-        return Configuration.gateway().credit_card.confirm_transparent_redirect(query_string)
-
-    @staticmethod
-    def create(params={}):
+    def create(params=None):
         """
         Create a CreditCard.
 
@@ -135,11 +124,12 @@ class CreditCard(Resource):
             })
 
         """
-
+        if params is None:
+            params = {}
         return Configuration.gateway().credit_card.create(params)
 
     @staticmethod
-    def update(credit_card_token, params={}):
+    def update(credit_card_token, params=None):
         """
         Update an existing CreditCard
 
@@ -150,7 +140,8 @@ class CreditCard(Resource):
             })
 
         """
-
+        if params is None:
+            params = {}
         return Configuration.gateway().credit_card.update(credit_card_token, params)
 
     @staticmethod
@@ -187,14 +178,6 @@ class CreditCard(Resource):
         """
         return Configuration.gateway().credit_card.find(credit_card_token)
 
-    @staticmethod
-    def forward(credit_card_token, receiving_merchant_id):
-        """
-        This method has been deprecated. Please consider the Grant API instead.
-        """
-
-        return Configuration.gateway().credit_card.forward(credit_card_token, receiving_merchant_id)
-
     @staticmethod
     def from_nonce(nonce):
         """
@@ -232,13 +215,14 @@ class CreditCard(Resource):
         ]
 
         options = [
+            "fail_on_duplicate_payment_method",
             "make_default",
+            "skip_advanced_fraud_checking",
+            "venmo_sdk_session",
+            "verification_account_type",
+            "verification_amount",
             "verification_merchant_account_id",
             "verify_card",
-            "verification_amount",
-            "verification_account_type",
-            "venmo_sdk_session",
-            "fail_on_duplicate_payment_method",
             {
                 "adyen":[
                     "overwrite_brand",
@@ -247,6 +231,14 @@ class CreditCard(Resource):
             }
         ]
 
+        three_d_secure_pass_thru = [
+            "cavv",
+            "ds_transaction_id",
+            "eci_flag",
+            "three_d_secure_version",
+            "xid"
+        ]
+
         signature = [
             "billing_address_id",
             "cardholder_name",
@@ -254,18 +246,20 @@ class CreditCard(Resource):
             "expiration_date",
             "expiration_month",
             "expiration_year",
-            "device_session_id",
-            "fraud_merchant_id",
             "number",
             "token",
             "venmo_sdk_payment_method_code",
             "device_data",
             "payment_method_nonce",
+            "device_session_id", "fraud_merchant_id", # NEXT_MAJOR_VERSION remove device_session_id and fraud_merchant_id
             {
                 "billing_address": billing_address_params
             },
             {
                 "options": options
+            },
+            {
+                "three_d_secure_pass_thru": three_d_secure_pass_thru
             }
         ]
 
@@ -281,38 +275,6 @@ class CreditCard(Resource):
 
         return signature
 
-    @staticmethod
-    def transparent_redirect_create_url():
-        """
-        Returns the url to use for creating CreditCards through transparent redirect.
-        """
-        warnings.warn("Please use TransparentRedirect.url instead", DeprecationWarning)
-        return Configuration.gateway().credit_card.transparent_redirect_create_url()
-
-    @staticmethod
-    def tr_data_for_create(tr_data, redirect_url):
-        """
-        Builds tr_data for CreditCard creation.
-        """
-
-        return Configuration.gateway().credit_card.tr_data_for_create(tr_data, redirect_url)
-
-    @staticmethod
-    def tr_data_for_update(tr_data, redirect_url):
-        """
-        Builds tr_data for CreditCard updating.
-        """
-
-        return Configuration.gateway().credit_card.tr_data_for_update(tr_data, redirect_url)
-
-    @staticmethod
-    def transparent_redirect_update_url():
-        """
-        Returns the url to be used for updating CreditCards through transparent redirect.
-        """
-        warnings.warn("Please use TransparentRedirect.url instead", DeprecationWarning)
-        return Configuration.gateway().credit_card.transparent_redirect_update_url()
-
     def __init__(self, gateway, attributes):
         Resource.__init__(self, gateway, attributes)
         self.is_expired = self.expired
@@ -339,4 +301,3 @@ class CreditCard(Resource):
         Returns the masked number of the CreditCard.
         """
         return self.bin + "******" + self.last_4
-
diff --git a/braintree/credit_card_gateway.py b/braintree/credit_card_gateway.py
index 4002701..18adec5 100644
--- a/braintree/credit_card_gateway.py
+++ b/braintree/credit_card_gateway.py
@@ -1,4 +1,5 @@
 import braintree
+import warnings
 from braintree.credit_card import CreditCard
 from braintree.error_result import ErrorResult
 from braintree.exceptions.not_found_error import NotFoundError
@@ -6,19 +7,18 @@ from braintree.ids_search import IdsSearch
 from braintree.resource import Resource
 from braintree.resource_collection import ResourceCollection
 from braintree.successful_result import SuccessfulResult
-from braintree.transparent_redirect import TransparentRedirect
+
 
 class CreditCardGateway(object):
     def __init__(self, gateway):
         self.gateway = gateway
         self.config = gateway.config
 
-    def confirm_transparent_redirect(self, query_string):
-        id = self.gateway.transparent_redirect._parse_and_validate_query_string(query_string)["id"][0]
-        return self._post("/payment_methods/all/confirm_transparent_redirect_request", {"id": id})
-
-    def create(self, params={}):
+    def create(self, params=None):
+        if params is None:
+            params = {}
         Resource.verify_keys(params, CreditCard.create_signature())
+        self.__check_for_deprecated_attributes(params)
         return self._post("/payment_methods", {"credit_card": params})
 
     def delete(self, credit_card_token):
@@ -57,24 +57,11 @@ class CreditCardGateway(object):
         except NotFoundError:
             raise NotFoundError("payment method with nonce " + repr(nonce) + " locked, consumed or not found")
 
-    def tr_data_for_create(self, tr_data, redirect_url):
-        Resource.verify_keys(tr_data, [{"credit_card": CreditCard.create_signature()}])
-        tr_data["kind"] = TransparentRedirect.Kind.CreatePaymentMethod
-        return self.gateway.transparent_redirect.tr_data(tr_data, redirect_url)
-
-    def tr_data_for_update(self, tr_data, redirect_url):
-        Resource.verify_keys(tr_data, ["payment_method_token", {"credit_card": CreditCard.update_signature()}])
-        tr_data["kind"] = TransparentRedirect.Kind.UpdatePaymentMethod
-        return self.gateway.transparent_redirect.tr_data(tr_data, redirect_url)
-
-    def transparent_redirect_create_url(self):
-        return self.config.base_url() + self.config.base_merchant_path() + "/payment_methods/all/create_via_transparent_redirect_request"
-
-    def transparent_redirect_update_url(self):
-        return self.config.base_url() + self.config.base_merchant_path() + "/payment_methods/all/update_via_transparent_redirect_request"
-
-    def update(self, credit_card_token, params={}):
+    def update(self, credit_card_token, params=None):
+        if params is None:
+            params = {}
         Resource.verify_keys(params, CreditCard.update_signature())
+        self.__check_for_deprecated_attributes(params)
         response = self.config.http().put(self.config.base_merchant_path() + "/payment_methods/credit_card/" + credit_card_token, {"credit_card": params})
         if "credit_card" in response:
             return SuccessfulResult({"credit_card": CreditCard(self.gateway, response["credit_card"])})
@@ -93,10 +80,17 @@ class CreditCardGateway(object):
         response = self.config.http().post(self.config.base_merchant_path() + "/payment_methods/all/expiring?" + query, {"search": criteria})
         return [CreditCard(self.gateway, item) for item in ResourceCollection._extract_as_array(response["payment_methods"], "credit_card")]
 
-    def _post(self, url, params={}):
+    def _post(self, url, params=None):
+        if params is None:
+            params = {}
         response = self.config.http().post(self.config.base_merchant_path() + url, params)
         if "credit_card" in response:
             return SuccessfulResult({"credit_card": CreditCard(self.gateway, response["credit_card"])})
         elif "api_error_response" in response:
             return ErrorResult(self.gateway, response["api_error_response"])
 
+    def __check_for_deprecated_attributes(self, params):
+        if "device_session_id" in params.keys():
+            warnings.warn("device_session_id is deprecated, use device_data parameter instead", DeprecationWarning)
+        if "fraud_merchant_id" in params.keys():
+            warnings.warn("fraud_merchant_id is deprecated, use device_data parameter instead", DeprecationWarning)
diff --git a/braintree/credit_card_verification.py b/braintree/credit_card_verification.py
index b081433..f397c26 100644
--- a/braintree/credit_card_verification.py
+++ b/braintree/credit_card_verification.py
@@ -7,6 +7,7 @@ from braintree.resource import Resource
 
 class CreditCardVerification(AttributeGetter):
 
+    # NEXT_MAJOR_VERSION this can be an enum! they were added as of python 3.4 and we support 3.5+
     class Status(object):
         """
         Constants representing transaction statuses. Available statuses are:
@@ -14,20 +15,18 @@ class CreditCardVerification(AttributeGetter):
         * braintree.CreditCardVerification.Status.Failed
         * braintree.CreditCardVerification.Status.GatewayRejected
         * braintree.CreditCardVerification.Status.ProcessorDeclined
-        * braintree.CreditCardVerification.Status.Unrecognized
         * braintree.CreditCardVerification.Status.Verified
         """
 
         Failed                 = "failed"
         GatewayRejected        = "gateway_rejected"
         ProcessorDeclined      = "processor_declined"
-        Unrecognized           = "unrecognized"
         Verified               = "verified"
 
     def __init__(self, gateway, attributes):
         AttributeGetter.__init__(self, attributes)
 
-        if "amount" in attributes and self.amount:
+        if "amount" in attributes and getattr(self, "amount", None):
             self.amount = Decimal(self.amount)
         else:
             self.amount = None
diff --git a/braintree/credit_card_verification_gateway.py b/braintree/credit_card_verification_gateway.py
index 40b69e8..3025bc7 100644
--- a/braintree/credit_card_verification_gateway.py
+++ b/braintree/credit_card_verification_gateway.py
@@ -6,6 +6,7 @@ from braintree.resource_collection import ResourceCollection
 from braintree.error_result import ErrorResult
 from braintree.successful_result import SuccessfulResult
 
+
 class CreditCardVerificationGateway(object):
     def __init__(self, gateway):
         self.gateway = gateway
@@ -51,8 +52,8 @@ class CreditCardVerificationGateway(object):
         return [CreditCardVerification(self.gateway, item) for item in ResourceCollection._extract_as_array(response["credit_card_verifications"], "verification")]
 
     def create(self, params):
-       response = self.config.http().post(self.config.base_merchant_path() + "/verifications", {"verification": params})
-       if "verification" in response:
-           return SuccessfulResult({"verification": CreditCardVerification(self.gateway, response["verification"])})
-       elif "api_error_response" in response:
-           return ErrorResult(self.gateway, response["api_error_response"])
+        response = self.config.http().post(self.config.base_merchant_path() + "/verifications", {"verification": params})
+        if "verification" in response:
+            return SuccessfulResult({"verification": CreditCardVerification(self.gateway, response["verification"])})
+        elif "api_error_response" in response:
+            return ErrorResult(self.gateway, response["api_error_response"])
diff --git a/braintree/credit_card_verification_search.py b/braintree/credit_card_verification_search.py
index af26c8b..8a89cf2 100644
--- a/braintree/credit_card_verification_search.py
+++ b/braintree/credit_card_verification_search.py
@@ -15,3 +15,4 @@ class CreditCardVerificationSearch:
     billing_postal_code          = Search.TextNodeBuilder("billing_address_details_postal_code")
     customer_email               = Search.TextNodeBuilder("customer_email")
     customer_id                  = Search.TextNodeBuilder("customer_id")
+    payment_method_token         = Search.TextNodeBuilder("payment_method_token")
diff --git a/braintree/customer.py b/braintree/customer.py
index 4c9b0ec..2d76c1e 100644
--- a/braintree/customer.py
+++ b/braintree/customer.py
@@ -8,9 +8,9 @@ from braintree.android_pay_card import AndroidPayCard
 from braintree.amex_express_checkout_card import AmexExpressCheckoutCard
 from braintree.credit_card import CreditCard
 from braintree.paypal_account import PayPalAccount
+from braintree.sepa_direct_debit_account import SepaDirectDebitAccount
 from braintree.europe_bank_account import EuropeBankAccount
 from braintree.us_bank_account import UsBankAccount
-from braintree.coinbase_account import CoinbaseAccount
 from braintree.venmo_account import VenmoAccount
 from braintree.visa_checkout_card import VisaCheckoutCard
 from braintree.masterpass_card import MasterpassCard
@@ -19,9 +19,9 @@ from braintree.configuration import Configuration
 from braintree.ids_search import IdsSearch
 from braintree.exceptions.not_found_error import NotFoundError
 from braintree.resource_collection import ResourceCollection
-from braintree.transparent_redirect import TransparentRedirect
 from braintree.samsung_pay_card import SamsungPayCard
 
+
 class Customer(Resource):
     """
     A class representing a customer.
@@ -67,13 +67,14 @@ class Customer(Resource):
         print(result.customer.id)
         print(result.customer.first_name)
 
-    For more information on Customers, see https://developers.braintreepayments.com/reference/request/customer/create/python
+    For more information on Customers, see https://developer.paypal.com/braintree/docs/reference/request/customer/create/python
 
     """
 
     def __repr__(self):
         detail_list = [
             "id",
+            "graphql_id",
             "company",
             "created_at",
             "email",
@@ -94,19 +95,7 @@ class Customer(Resource):
         return Configuration.gateway().customer.all()
 
     @staticmethod
-    def confirm_transparent_redirect(query_string):
-        """
-        Confirms a transparent redirect request.  It expects the query string from the
-        redirect request.  The query string should _not_ include the leading "?" character. ::
-
-            result = braintree.Customer.confirm_transparent_redirect_request("foo=bar&id=12345")
-        """
-
-        warnings.warn("Please use TransparentRedirect.confirm instead", DeprecationWarning)
-        return Configuration.gateway().customer.confirm_transparent_redirect(query_string)
-
-    @staticmethod
-    def create(params={}):
+    def create(params=None):
         """
         Create a Customer
 
@@ -118,7 +107,8 @@ class Customer(Resource):
             })
 
         """
-
+        if params is None:
+            params = {}
         return Configuration.gateway().customer.create(params)
 
     @staticmethod
@@ -151,33 +141,7 @@ class Customer(Resource):
         return Configuration.gateway().customer.search(*query)
 
     @staticmethod
-    def tr_data_for_create(tr_data, redirect_url):
-        """ Builds tr_data for creating a Customer. """
-
-        return Configuration.gateway().customer.tr_data_for_create(tr_data, redirect_url)
-
-    @staticmethod
-    def tr_data_for_update(tr_data, redirect_url):
-        """ Builds tr_data for updating a Customer. """
-
-        return Configuration.gateway().customer.tr_data_for_update(tr_data, redirect_url)
-
-    @staticmethod
-    def transparent_redirect_create_url():
-        """ Returns the url to use for creating Customers through transparent redirect. """
-
-        warnings.warn("Please use TransparentRedirect.url instead", DeprecationWarning)
-        return Configuration.gateway().customer.transparent_redirect_create_url()
-
-    @staticmethod
-    def transparent_redirect_update_url():
-        """ Returns the url to use for updating Customers through transparent redirect. """
-
-        warnings.warn("Please use TransparentRedirect.url instead", DeprecationWarning)
-        return Configuration.gateway().customer.transparent_redirect_update_url()
-
-    @staticmethod
-    def update(customer_id, params={}):
+    def update(customer_id, params=None):
         """
         Update an existing Customer
 
@@ -188,16 +152,27 @@ class Customer(Resource):
             })
 
         """
-
+        if params is None:
+            params = {}
         return Configuration.gateway().customer.update(customer_id, params)
 
     @staticmethod
     def create_signature():
         return [
-            "company", "email", "fax", "first_name", "id", "last_name", "phone", "website", "device_data", "device_session_id", "fraud_merchant_id", "payment_method_nonce",
-            {"risk_data": ["customer_browser", "customer_ip"]},
+            "company", "email", "fax", "first_name", "id", "last_name", "phone", "website", "device_data", "payment_method_nonce",
+            "device_session_id", "fraud_merchant_id", # NEXT_MAJOR_VERSION remove device_session_id and fraud_merchant_id
+            {"risk_data": ["customer_browser", "customer_device_id", "customer_ip", "customer_location_zip", "customer_tenure"]},
             {"credit_card": CreditCard.create_signature()},
+            {"apple_pay_card": ApplePayCard.signature()},
             {"custom_fields": ["__any_key__"]},
+            {"three_d_secure_pass_thru": [
+                "cavv",
+                "ds_transaction_id",
+                "eci_flag",
+                "three_d_secure_version",
+                "xid",
+                ]},
+            {"tax_identifiers": ["country_code", "identifier"]},
             {"options": [{"paypal": [
                 "payee_email",
                 "order_id",
@@ -213,7 +188,16 @@ class Customer(Resource):
         return [
             "company", "email", "fax", "first_name", "id", "last_name", "phone", "website", "device_data", "device_session_id", "fraud_merchant_id", "payment_method_nonce", "default_payment_method_token",
             {"credit_card": CreditCard.signature("update_via_customer")},
+            {"apple_pay_card": ApplePayCard.signature()},
+            {"three_d_secure_pass_thru": [
+                "cavv",
+                "ds_transaction_id",
+                "eci_flag",
+                "three_d_secure_version",
+                "xid",
+                ]},
             {"custom_fields": ["__any_key__"]},
+            {"tax_identifiers": ["country_code", "identifier"]},
             {"options": [{"paypal": [
                 "payee_email",
                 "order_id",
@@ -246,6 +230,7 @@ class Customer(Resource):
             self.android_pay_cards  = [AndroidPayCard(gateway, android_pay_card) for android_pay_card in self.android_pay_cards]
             self.payment_methods += self.android_pay_cards
 
+        # NEXT_MAJOR_VERSION remove deprecated amex express checkout
         if "amex_express_checkout_cards" in attributes:
             self.amex_express_checkout_cards  = [AmexExpressCheckoutCard(gateway, amex_express_checkout_card) for amex_express_checkout_card in self.amex_express_checkout_cards]
             self.payment_methods += self.amex_express_checkout_cards
@@ -254,14 +239,14 @@ class Customer(Resource):
             self.europe_bank_accounts = [EuropeBankAccount(gateway, europe_bank_account) for europe_bank_account in self.europe_bank_accounts]
             self.payment_methods += self.europe_bank_accounts
 
-        if "coinbase_accounts" in attributes:
-            self.coinbase_accounts = [CoinbaseAccount(gateway, coinbase_account) for coinbase_account in self.coinbase_accounts]
-            self.payment_methods += self.coinbase_accounts
-
         if "venmo_accounts" in attributes:
             self.venmo_accounts = [VenmoAccount(gateway, venmo_account) for venmo_account in self.venmo_accounts]
             self.payment_methods += self.venmo_accounts
 
+        if "sepa_debit_accounts" in attributes:
+            self.sepa_direct_debit_accounts  = [SepaDirectDebitAccount(gateway, sepa_direct_debit_account) for sepa_direct_debit_account in self.sepa_debit_accounts]
+            self.payment_methods += self.sepa_direct_debit_accounts
+
         if "us_bank_accounts" in attributes:
             self.us_bank_accounts = [UsBankAccount(gateway, us_bank_account) for us_bank_account in self.us_bank_accounts]
             self.payment_methods += self.us_bank_accounts
@@ -270,6 +255,7 @@ class Customer(Resource):
             self.visa_checkout_cards = [VisaCheckoutCard(gateway, visa_checkout_card) for visa_checkout_card in self.visa_checkout_cards]
             self.payment_methods += self.visa_checkout_cards
 
+        # NEXT_MAJOR_VERSION remove deprecated masterpass
         if "masterpass_cards" in attributes:
             self.masterpass_cards = [MasterpassCard(gateway, masterpass_card) for masterpass_card in self.masterpass_cards]
             self.payment_methods += self.masterpass_cards
diff --git a/braintree/customer_gateway.py b/braintree/customer_gateway.py
index 6ea6b64..3c494ea 100644
--- a/braintree/customer_gateway.py
+++ b/braintree/customer_gateway.py
@@ -1,4 +1,5 @@
 import braintree
+import warnings
 from braintree.customer import Customer
 from braintree.error_result import ErrorResult
 from braintree.exceptions.not_found_error import NotFoundError
@@ -6,7 +7,7 @@ from braintree.ids_search import IdsSearch
 from braintree.resource import Resource
 from braintree.resource_collection import ResourceCollection
 from braintree.successful_result import SuccessfulResult
-from braintree.transparent_redirect import TransparentRedirect
+
 
 class CustomerGateway(object):
     def __init__(self, gateway):
@@ -17,12 +18,11 @@ class CustomerGateway(object):
         response = self.config.http().post(self.config.base_merchant_path() + "/customers/advanced_search_ids")
         return ResourceCollection({}, response, self.__fetch)
 
-    def confirm_transparent_redirect(self, query_string):
-        id = self.gateway.transparent_redirect._parse_and_validate_query_string(query_string)["id"][0]
-        return self._post("/customers/all/confirm_transparent_redirect_request", {"id": id})
-
-    def create(self, params={}):
+    def create(self, params=None):
+        if params is None:
+            params = {}
         Resource.verify_keys(params, Customer.create_signature())
+        self.__check_for_deprecated_attributes(params)
         return self._post("/customers", {"customer": params})
 
     def delete(self, customer_id):
@@ -50,24 +50,11 @@ class CustomerGateway(object):
         response = self.config.http().post(self.config.base_merchant_path() + "/customers/advanced_search_ids", {"search": self.__criteria(query)})
         return ResourceCollection(query, response, self.__fetch)
 
-    def tr_data_for_create(self, tr_data, redirect_url):
-        Resource.verify_keys(tr_data, [{"customer": Customer.create_signature()}])
-        tr_data["kind"] = TransparentRedirect.Kind.CreateCustomer
-        return self.gateway.transparent_redirect.tr_data(tr_data, redirect_url)
-
-    def tr_data_for_update(self, tr_data, redirect_url):
-        Resource.verify_keys(tr_data, ["customer_id", {"customer": Customer.update_signature()}])
-        tr_data["kind"] = TransparentRedirect.Kind.UpdateCustomer
-        return self.gateway.transparent_redirect.tr_data(tr_data, redirect_url)
-
-    def transparent_redirect_create_url(self):
-        return self.config.base_url() + self.config.base_merchant_path() + "/customers/all/create_via_transparent_redirect_request"
-
-    def transparent_redirect_update_url(self):
-        return self.config.base_url() + self.config.base_merchant_path() + "/customers/all/update_via_transparent_redirect_request"
-
-    def update(self, customer_id, params={}):
+    def update(self, customer_id, params=None):
+        if params is None:
+            params = {}
         Resource.verify_keys(params, Customer.update_signature())
+        self.__check_for_deprecated_attributes(params)
         response = self.config.http().put(self.config.base_merchant_path() + "/customers/" + customer_id, {"customer": params})
         if "customer" in response:
             return SuccessfulResult({"customer": Customer(self.gateway, response["customer"])})
@@ -89,7 +76,9 @@ class CustomerGateway(object):
         response = self.config.http().post(self.config.base_merchant_path() + "/customers/advanced_search", {"search": criteria})
         return [Customer(self.gateway, item) for item in ResourceCollection._extract_as_array(response["customers"], "customer")]
 
-    def _post(self, url, params={}):
+    def _post(self, url, params=None):
+        if params is None:
+            params = {}
         response = self.config.http().post(self.config.base_merchant_path() + url, params)
         if "customer" in response:
             return SuccessfulResult({"customer": Customer(self.gateway, response["customer"])})
@@ -98,3 +87,8 @@ class CustomerGateway(object):
         else:
             pass
 
+    def __check_for_deprecated_attributes(self, params):
+        if "device_session_id" in params.keys():
+            warnings.warn("device_session_id is deprecated, use device_data parameter instead", DeprecationWarning)
+        if "fraud_merchant_id" in params.keys():
+            warnings.warn("fraud_merchant_id is deprecated, use device_data parameter instead", DeprecationWarning)
diff --git a/braintree/disbursement_detail.py b/braintree/disbursement_detail.py
index b8c9a84..e1bf221 100644
--- a/braintree/disbursement_detail.py
+++ b/braintree/disbursement_detail.py
@@ -5,9 +5,9 @@ class DisbursementDetail(AttributeGetter):
     def __init__(self, attributes):
         AttributeGetter.__init__(self, attributes)
 
-        if self.settlement_amount is not None:
+        if getattr(self, "settlement_amount", None) is not None:
             self.settlement_amount = Decimal(self.settlement_amount)
-        if self.settlement_currency_exchange_rate is not None:
+        if getattr(self, "settlement_currency_exchange_rate", None) is not None:
             self.settlement_currency_exchange_rate = Decimal(self.settlement_currency_exchange_rate)
 
     @property
diff --git a/braintree/dispute.py b/braintree/dispute.py
index 2049bf9..179e4c2 100644
--- a/braintree/dispute.py
+++ b/braintree/dispute.py
@@ -1,27 +1,32 @@
+import warnings
 from decimal import Decimal
 from braintree.attribute_getter import AttributeGetter
 from braintree.transaction_details import TransactionDetails
-from braintree.dispute_details import DisputeEvidence, DisputeStatusHistory
+from braintree.dispute_details import DisputeEvidence, DisputeStatusHistory, DisputePayPalMessage
 from braintree.configuration import Configuration
 
 class Dispute(AttributeGetter):
+    # NEXT_MAJOR_VERSION this can be an enum! they were added as of python 3.4 and we support 3.5+
     class Status(object):
         """
         Constants representing dispute statuses. Available types are:
 
         * braintree.Dispute.Status.Accepted
+        * braintree.Dispute.Status.AutoAccepted
         * braintree.Dispute.Status.Disputed
         * braintree.Dispute.Status.Open
         * braintree.Dispute.Status.Won
         * braintree.Dispute.Status.Lost
         """
         Accepted = "accepted"
+        AutoAccepted = "auto_accepted"
         Disputed = "disputed"
         Expired = "expired"
         Open  = "open"
         Won  = "won"
         Lost = "lost"
 
+    # NEXT_MAJOR_VERSION this can be an enum! they were added as of python 3.4 and we support 3.5+
     class Reason(object):
         """
         Constants representing dispute reasons. Available types are:
@@ -50,6 +55,7 @@ class Dispute(AttributeGetter):
         Retrieval                     = "retrieval"
         TransactionAmountDiffers      = "transaction_amount_differs"
 
+    # NEXT_MAJOR_VERSION this can be an enum! they were added as of python 3.4 and we support 3.5+
     class Kind(object):
         """
         Constants representing dispute kinds. Available types are:
@@ -62,6 +68,44 @@ class Dispute(AttributeGetter):
         PreArbitration = "pre_arbitration"
         Retrieval      = "retrieval"
 
+    # NEXT_MAJOR_VERSION Remove this enum
+    class ChargebackProtectionLevel(object):
+        """
+        Constants representing dispute ChargebackProtectionLevel. Available types are:
+
+        * braintree.Dispute.ChargebackProtectionLevel.EFFORTLESS
+        * braintree.Dispute.ChargebackProtectionLevel.STANDARD
+        * braintree.Dispute.ChargebackProtectionLevel.NOT_PROTECTED
+        """
+        warnings.warn("Use ProtectionLevel enum instead", DeprecationWarning)
+        Effortless     = "effortless"
+        Standard       = "standard"
+        NotProtected   = "not_protected"
+
+    # NEXT_MAJOR_VERSION this can be an enum! they were added as of python 3.4 and we support 3.5+
+    class PreDisputeProgram(object):
+        """
+        Constants representing dispute pre-dispute programs. Available types are:
+
+        * braintree.Dispute.PreDisputeProgram.NONE
+        * braintree.Dispute.PreDisputeProgram.VisaRdr
+        """
+        NONE = "none"
+        VisaRdr = "visa_rdr"
+
+    # NEXT_MAJOR_VERSION this can be an enum! they were added as of python 3.4 and we support 3.5+
+    class ProtectionLevel(object):
+        """
+        Constants representing dispute ProtectionLevel. Available types are:
+
+        * braintree.Dispute.ProtectionLevel.EffortlessCBP
+        * braintree.Dispute.ProtectionLevel.StandardCBP
+        * braintree.Dispute.ProtectionLevel.NoProtection
+        """
+        EffortlessCBP  = "Effortless Chargeback Protection tool"
+        StandardCBP    = "Chargeback Protection tool"
+        NoProtection   = "No Protection"
+
     @staticmethod
     def accept(id):
         """
@@ -160,20 +204,28 @@ class Dispute(AttributeGetter):
         return Configuration.gateway().dispute.search(*query)
 
     def __init__(self, attributes):
+        if "chargeback_protection_level" in attributes:
+            warnings.warn("Use protection_level attribute instead", DeprecationWarning)
         AttributeGetter.__init__(self, attributes)
 
-        if "amount" in attributes and self.amount is not None:
+        if "amount" in attributes and getattr(self, "amount", None) is not None:
             self.amount = Decimal(self.amount)
-        if "amount_disputed" in attributes and self.amount_disputed is not None:
+        if "amount_disputed" in attributes and getattr(self, "amount_disputed", None) is not None:
             self.amount_disputed = Decimal(self.amount_disputed)
-        if "amount_won" in attributes and self.amount_won is not None:
+        if "amount_won" in attributes and getattr(self, "amount_won", None) is not None:
             self.amount_won = Decimal(self.amount_won)
+        if "chargeback_protection_level" in attributes and getattr(self, "chargeback_protection_level", None) in [self.ChargebackProtectionLevel.Effortless, self.ChargebackProtectionLevel.Standard]:
+            self.protection_level = eval("self.ProtectionLevel.{0}CBP".format(self.chargeback_protection_level.capitalize()))
+        else:
+            self.protection_level = self.ProtectionLevel.NoProtection
         if "transaction" in attributes:
             self.transaction_details = TransactionDetails(attributes.pop("transaction"))
             self.transaction = self.transaction_details
-        if "evidence" in attributes and self.evidence is not None:
+        if "evidence" in attributes and getattr(self, "evidence", None) is not None:
             self.evidence = [DisputeEvidence(evidence) for evidence in self.evidence]
-        if "status_history" in attributes and self.status_history is not None:
+        if "paypal_messages" in attributes and getattr(self, "paypal_messages", None) is not None:
+            self.paypal_messages = [DisputePayPalMessage(paypal_message) for paypal_message in self.paypal_messages]
+        if "status_history" in attributes and getattr(self, "status_history", None) is not None:
             self.status_history = [DisputeStatusHistory(status_history) for status_history in self.status_history]
         if "processor_comments" in attributes and self.processor_comments is not None:
             self.forwarded_comments = self.processor_comments
diff --git a/braintree/dispute_details/__init__.py b/braintree/dispute_details/__init__.py
index 5cde7c2..696bfe9 100644
--- a/braintree/dispute_details/__init__.py
+++ b/braintree/dispute_details/__init__.py
@@ -1,2 +1,3 @@
 from braintree.dispute_details.evidence import DisputeEvidence
+from braintree.dispute_details.paypal_message import DisputePayPalMessage
 from braintree.dispute_details.status_history import DisputeStatusHistory
diff --git a/braintree/dispute_details/paypal_message.py b/braintree/dispute_details/paypal_message.py
new file mode 100644
index 0000000..2b447ed
--- /dev/null
+++ b/braintree/dispute_details/paypal_message.py
@@ -0,0 +1,5 @@
+from braintree.attribute_getter import AttributeGetter
+
+class DisputePayPalMessage(AttributeGetter):
+    def __init__(self, attributes):
+        AttributeGetter.__init__(self, attributes)
diff --git a/braintree/dispute_gateway.py b/braintree/dispute_gateway.py
index 3cfe88e..902dcf1 100644
--- a/braintree/dispute_gateway.py
+++ b/braintree/dispute_gateway.py
@@ -73,19 +73,14 @@ class DisputeGateway(object):
         except ValueError:
             raise ValueError("sequence_number must be an integer")
 
-        category = request.get("category", request.get("tag"))
-
-        if "tag" in request.keys():
-            warnings.warn("Please use category instead", DeprecationWarning)
-
-        if category is not None and not isinstance(category, str):
+        if request.get("category") is not None and not isinstance(request.get("category"), str):
             raise ValueError("category must be a string")
 
         try:
             response = self.config.http().post(self.config.base_merchant_path() + "/disputes/" + dispute_id + "/evidence", {
                 "evidence": {
                     "comments": request.get("content"),
-                    "category": category,
+                    "category": request.get("category"),
                     "sequence_number": request.get("sequence_number")
                 }
             })
diff --git a/braintree/dispute_search.py b/braintree/dispute_search.py
index 11f53df..821d71b 100644
--- a/braintree/dispute_search.py
+++ b/braintree/dispute_search.py
@@ -1,20 +1,25 @@
 from braintree.search import Search
 
 class DisputeSearch:
-    amount_disputed     = Search.RangeNodeBuilder("amount_disputed")
-    amount_won          = Search.RangeNodeBuilder("amount_won")
-    case_number         = Search.TextNodeBuilder("case_number")
-    customer_id         = Search.TextNodeBuilder("customer_id")
-    disbursement_date   = Search.RangeNodeBuilder("disbursement_date")
-    effective_date      = Search.RangeNodeBuilder("effective_date")
-    id                  = Search.TextNodeBuilder("id")
-    kind                = Search.MultipleValueNodeBuilder("kind")
-    merchant_account_id = Search.MultipleValueNodeBuilder("merchant_account_id")
-    reason              = Search.MultipleValueNodeBuilder("reason")
-    reason_code         = Search.MultipleValueNodeBuilder("reason_code")
-    received_date       = Search.RangeNodeBuilder("received_date")
-    reference_number    = Search.TextNodeBuilder("reference_number")
-    reply_by_date       = Search.RangeNodeBuilder("reply_by_date")
-    status              = Search.MultipleValueNodeBuilder("status")
-    transaction_id      = Search.TextNodeBuilder("transaction_id")
-    transaction_source  = Search.MultipleValueNodeBuilder("transaction_source")
+    amount_disputed             =   Search.RangeNodeBuilder("amount_disputed")
+    amount_won                  =   Search.RangeNodeBuilder("amount_won")
+    case_number                 =   Search.TextNodeBuilder("case_number")
+    # NEXT_MAJOR_VERSION Remove this attribute
+    # DEPRECATED The chargeback_protection_level attribute is deprecated in favor of protection_level
+    chargeback_protection_level =   Search.MultipleValueNodeBuilder("chargeback_protection_level")
+    protection_level            =   Search.MultipleValueNodeBuilder("protection_level")
+    customer_id                 =   Search.TextNodeBuilder("customer_id")
+    disbursement_date           =   Search.RangeNodeBuilder("disbursement_date")
+    effective_date              =   Search.RangeNodeBuilder("effective_date")
+    id                          =   Search.TextNodeBuilder("id")
+    kind                        =   Search.MultipleValueNodeBuilder("kind")
+    merchant_account_id         =   Search.MultipleValueNodeBuilder("merchant_account_id")
+    pre_dispute_program         =   Search.MultipleValueNodeBuilder("pre_dispute_program")
+    reason                      =   Search.MultipleValueNodeBuilder("reason")
+    reason_code                 =   Search.MultipleValueNodeBuilder("reason_code")
+    received_date               =   Search.RangeNodeBuilder("received_date")
+    reference_number            =   Search.TextNodeBuilder("reference_number")
+    reply_by_date               =   Search.RangeNodeBuilder("reply_by_date")
+    status                      =   Search.MultipleValueNodeBuilder("status")
+    transaction_id              =   Search.TextNodeBuilder("transaction_id")
+    transaction_source          =   Search.MultipleValueNodeBuilder("transaction_source")
diff --git a/braintree/document_upload.py b/braintree/document_upload.py
index 4a93f4e..55b794f 100644
--- a/braintree/document_upload.py
+++ b/braintree/document_upload.py
@@ -3,6 +3,7 @@ from braintree.successful_result import SuccessfulResult
 from braintree.resource import Resource
 from braintree.configuration import Configuration
 
+
 class DocumentUpload(Resource):
     """
     A class representing a DocumentUpload.
@@ -16,7 +17,7 @@ class DocumentUpload(Resource):
             }
         )
 
-    For more information on DocumentUploads, see https://developers.braintreepayments.com/reference/request/document_upload/create
+    For more information on DocumentUploads, see https://developer.paypal.com/braintree/docs/reference/request/document-upload/create
 
     """
 
@@ -24,7 +25,7 @@ class DocumentUpload(Resource):
         EvidenceDocument = "evidence_document"
 
     @staticmethod
-    def create(params={}):
+    def create(params=None):
         """
         Create a DocumentUpload
 
@@ -38,6 +39,8 @@ class DocumentUpload(Resource):
             )
 
         """
+        if params is None:
+            params = {}
         return Configuration.gateway().document_upload.create(params)
 
     @staticmethod
diff --git a/braintree/document_upload_gateway.py b/braintree/document_upload_gateway.py
index 15aea8c..ddfcd54 100644
--- a/braintree/document_upload_gateway.py
+++ b/braintree/document_upload_gateway.py
@@ -5,12 +5,15 @@ from braintree.error_result import ErrorResult
 from braintree.resource import Resource
 from braintree.successful_result import SuccessfulResult
 
+
 class DocumentUploadGateway(object):
     def __init__(self, gateway):
         self.gateway = gateway
         self.config = gateway.config
 
-    def create(self, params={}):
+    def create(self, params=None):
+        if params is None:
+            params = {}
         Resource.verify_keys(params, DocumentUpload.create_signature())
 
         if "file" in params and not hasattr(params["file"], "read"):
diff --git a/braintree/enriched_customer_data.py b/braintree/enriched_customer_data.py
new file mode 100644
index 0000000..5d1cc74
--- /dev/null
+++ b/braintree/enriched_customer_data.py
@@ -0,0 +1,10 @@
+from braintree.resource import Resource
+from braintree.venmo_profile_data import VenmoProfileData
+
+class EnrichedCustomerData(Resource):
+    """
+    A class representing Braintree EnrichedCustomerData object.
+    """
+    def __init__(self, gateway, attributes):
+        Resource.__init__(self, gateway, attributes)
+        self.profile_data = VenmoProfileData(gateway, attributes.pop("profile_data"))
diff --git a/braintree/error_codes.py b/braintree/error_codes.py
index c5e0949..202dd8c 100644
--- a/braintree/error_codes.py
+++ b/braintree/error_codes.py
@@ -18,7 +18,6 @@ class ErrorCodes(object):
         CountryCodeAlpha3IsNotAccepted = "91816"
         CountryCodeNumericIsNotAccepted = "91817"
         CountryNameIsNotAccepted = "91803"
-        ExtedAddressIsTooLong = "81804" # Deprecated
         ExtendedAddressIsInvalid = "91823"
         ExtendedAddressIsTooLong = "81804"
         FirstNameIsInvalid = "91819"
@@ -65,6 +64,8 @@ class ErrorCodes(object):
         InvalidToken = "83520"
         PrivateKeyMismatch = "93521"
         KeyMismatchStoringCertificate = "93522"
+        CustomerIdIsInvalid = "93528"
+        BillingAddressFormatIsInvalid = "93529"
 
     class AuthorizationFingerprint(object):
         MissingFingerprint = "93201"
@@ -156,7 +157,6 @@ class ErrorCodes(object):
         FaxIsTooLong = "81607"
         FirstNameIsTooLong = "81608"
         IdIsInUse = "91609"
-        IdIsInvaild = "91610" # Deprecated
         IdIsInvalid = "91610"
         IdIsNotAllowed = "91611"
         IdIsRequired = "91613"
@@ -206,6 +206,7 @@ class ErrorCodes(object):
         FileTypeIsInvalid = "84903"
         FileIsMalformedOrEncrypted = "84904"
         FileIsTooLong = "84905"
+        FileIsEmpty = "84906"
 
     class Merchant(object):
         CountryCannotBeBlank = "83603"
@@ -337,6 +338,27 @@ class ErrorCodes(object):
         UnsupportedGrantType = "93805"
 
     class Verification(object):
+        ThreeDSecureAuthenticationIdIsInvalid = "942196"
+        ThreeDSecureAuthenticationIdDoesntMatchNonceThreeDSecureAuthentication = "942198"
+        ThreeDSecureTransactionPaymentMethodDoesntMatchThreeDSecureAuthenticationPaymentMethod = "942197"
+        ThreeDSecureAuthenticationIdWithThreeDSecurePassThruIsInvalid = "942199"
+        ThreeDSecureAuthenticationFailed = "94271"
+        ThreeDSecureTokenIsInvalid = "94268"
+        ThreeDSecureVerificationDataDoesntMatchVerify = "94270"
+        MerchantAccountDoesNotSupport3DSecure = "942169"
+        MerchantAcountDoesNotMatch3DSecureMerchantAccount = "94284"
+        AmountDoesNotMatch3DSecureAmount = "94285"
+        class ThreeDSecurePassThru(object):
+            EciFlagIsRequired = "942113"
+            EciFlagIsInvalid = "942114"
+            CavvIsRequired = "942116"
+            ThreeDSecureVersionIsRequired = "942117"
+            ThreeDSecureVersionIsInvalid = "942119"
+            AuthenticationResponseIsInvalid = "942120"
+            DirectoryResponseIsInvalid = "942121"
+            CavvAlgorithmIsInvalid = "942122"
+
+
         class Options(object):
             AmountCannotBeNegative = "94201"
             AmountFormatIsInvalid = "94202"
@@ -403,6 +425,11 @@ class ErrorCodes(object):
         IBANIsRequired = "83303"
         AccountHolderNameIsRequired = "83301"
 
+    class SepaDirectDebitAccount(object):
+        SepaDebitAccountPaymentMethodMandateTypeIsNotSupported = "87115"
+        SepaDebitAccountPaymentMethodCustomerIdIsInvalid = "87116"
+        SepaDebitAccountPaymentMethodCustomerIdIsRequired = "87117"
+
     class Subscription(object):
         BillingDayOfMonthCannotBeUpdated = "91918"
         BillingDayOfMonthIsInvalid = "91914"
@@ -469,15 +496,16 @@ class ErrorCodes(object):
             IdToRemoveIsInvalid = "92025"
 
     class Transaction(object):
+        AdjustmentAmountMustBeGreaterThanZero = "95605"
         AmountCannotBeNegative = "81501"
         AmountDoesNotMatch3DSecureAmount = "91585"
-        AmountDoesNotMatchIdealPaymentAmount = "915144"
         AmountIsInvalid = AmountFormatIsInvalid = "81503"
         AmountIsRequired = "81502"
         AmountIsTooLarge = "81528"
         AmountMustBeGreaterThanZero = "81531"
         AmountNotSupportedByProcessor = "815193"
         BillingAddressConflict = "91530"
+        BillingPhoneNumberIsInvalid = "915206"
         CannotBeVoided = "91504"
         CannotCancelRelease = "91562"
         CannotCloneCredit = "91543"
@@ -506,39 +534,29 @@ class ErrorCodes(object):
         CustomerDefaultPaymentMethodCardTypeIsNotAccepted = "81509"
         CustomerDoesNotHaveCreditCard = "91511"
         CustomerIdIsInvalid = "91510"
+        DiscountAmountCannotBeNegative = "915160"
+        DiscountAmountFormatIsInvalid = "915159"
+        DiscountAmountIsTooLarge = "915161"
+        ExchangeRateQuoteIdIsTooLong = "915229"
         FailedAuthAdjustmentAllowRetry = "95603"
         FailedAuthAdjustmentHardDecline = "95602"
         FinalAuthSubmitForSettlementForDifferentAmount = "95601"
         HasAlreadyBeenRefunded = "91512"
-        IdealPaymentNotComplete = "815141"
-        IdealPaymentsCannotBeVaulted = "915150"
-        PaymentInstrumentWithExternalVaultIsInvalid = "915176"
-        DiscountAmountFormatIsInvalid = "915159"
-        DiscountAmountCannotBeNegative = "915160"
-        DiscountAmountIsTooLarge = "915161"
-        ShippingAmountFormatIsInvalid = "915162"
-        ShippingAmountCannotBeNegative = "915163"
-        ShippingAmountIsTooLarge = "915164"
-        ShipsFromPostalCodeIsTooLong = "915165"
-        ShipsFromPostalCodeIsInvalid = "915166"
-        ShipsFromPostalCodeInvalidCharacters = "915167"
+        LineItemsExpected = "915158"
         MerchantAccountDoesNotMatch3DSecureMerchantAccount = "91584"
-        MerchantAccountDoesNotMatchIdealPaymentMerchantAccount = "915143"
         MerchantAccountDoesNotSupportMOTO = "91558"
         MerchantAccountDoesNotSupportRefunds = "91547"
         MerchantAccountIdDoesNotMatchSubscription = "915180"
         MerchantAccountIdIsInvalid = "91513"
-        MerchantAccountIsSusped = "91514" # Deprecated
         MerchantAccountIsSuspended = "91514"
-        MerchantAccountNameIsInvalid = "91513" # Deprecated
-        OrderIdDoesNotMatchIdealPaymentOrderId = "91503"
-        OrderIdIsRequiredWithIdealPayment = "91502"
+        NoNetAmountToPerformAuthAdjustment = "95606"
         OrderIdIsTooLong = "91501"
         PayPalAuthExpired = "91579"
         PayPalNotEnabled = "91576"
         PayPalVaultRecordMissingData = "91583"
         PaymentInstrumentNotSupportedByMerchantAccount = "91577"
         PaymentInstrumentTypeIsNotAccepted = "915101"
+        PaymentInstrumentWithExternalVaultIsInvalid = "915176"
         PaymentMethodConflict = "91515"
         PaymentMethodConflictWithVenmoSDK = "91549"
         PaymentMethodDoesNotBelongToCustomer = "91516"
@@ -553,15 +571,23 @@ class ErrorCodes(object):
         ProcessorAuthorizationCodeCannotBeSet = "91519"
         ProcessorAuthorizationCodeIsInvalid = "81520"
         ProcessorDoesNotSupportAuths = "915104"
+        ProcessorDoesNotSupportAuthAdjustment = "915222"
         ProcessorDoesNotSupportCredits = "91546"
+        ProcessorDoesNotSupportIncrementalAuth = "915220"
+        ProcessorDoesNotSupportMotoForCardType = "915195"
+        ProcessorDoesNotSupportPartialAuthReversal = "915221"
         ProcessorDoesNotSupportPartialSettlement = "915102"
-        ProcessorDoesNotSupportUpdatingOrderId = "915107"
         ProcessorDoesNotSupportUpdatingDescriptor = "915108"
+        ProcessorDoesNotSupportUpdatingOrderId = "915107"
         ProcessorDoesNotSupportUpdatingTransactionDetails = "915130"
         ProcessorDoesNotSupportVoiceAuthorizations = "91545"
+        ProductSkuIsInvalid = "915202"
         PurchaseOrderNumberIsInvalid = "91548"
         PurchaseOrderNumberIsTooLong = "91537"
         RefundAmountIsTooLarge = "91521"
+        RefundAuthHardDeclined = "915200"
+        RefundAuthSoftDeclined = "915201"
+        ScaExemptionInvalid = "915213"
         ServiceFeeAmountCannotBeNegative = "91554"
         ServiceFeeAmountFormatIsInvalid = "91555"
         ServiceFeeAmountIsTooLarge = "91556"
@@ -571,36 +597,51 @@ class ErrorCodes(object):
         SettlementAmountIsLessThanServiceFeeAmount = "91551"
         SettlementAmountIsTooLarge = "91522"
         ShippingAddressDoesntMatchCustomer = "91581"
+        ShippingAmountCannotBeNegative = "915163"
+        ShippingAmountFormatIsInvalid = "915162"
+        ShippingAmountIsTooLarge = "915164"
+        ShippingMethodIsInvalid = "915203"
+        ShippingPhoneNumberIsInvalid = "915204"
+        ShipsFromPostalCodeInvalidCharacters = "915167"
+        ShipsFromPostalCodeIsInvalid = "915166"
+        ShipsFromPostalCodeIsTooLong = "915165"
         SubMerchantAccountRequiresServiceFeeAmount = "91553"
         SubscriptionDoesNotBelongToCustomer = "91529"
         SubscriptionIdIsInvalid = "91528"
         SubscriptionStatusMustBePastDue = "91531"
         TaxAmountCannotBeNegative = "81534"
         TaxAmountFormatIsInvalid = "81535"
+        TaxAmountIsRequiredForAibSwedish = "815224"
         TaxAmountIsTooLarge = "81536"
         ThreeDSecureAuthenticationFailed = "81571"
-        ThreeDSecureTokenIsInvalid = "91568"
-        ThreeDSecureTransactionDataDoesntMatchVerify = "91570"
-        ThreeDSecureEciFlagIsRequired = "915113"
-        ThreeDSecureCavvIsRequired = "915116"
-        ThreeDSecureXidIsRequired = "915115"
-        ThreeDSecureEciFlagIsInvalid = "915114"
+        ThreeDSecureAuthenticationIdDoesntMatchNonceThreeDSecureAuthentication = "915198"
+        ThreeDSecureAuthenticationIdIsInvalid = "915196"
+        ThreeDSecureAuthenticationIdWithThreeDSecurePassThruIsInvalid = "915199"
         ThreeDSecureAuthenticationResponseIsInvalid = "915120"
-        ThreeDSecureDirectoryResponseIsInvalid = "915121"
         ThreeDSecureCavvAlgorithmIsInvalid = "915122"
+        ThreeDSecureCavvIsRequired = "915116"
+        ThreeDSecureDirectoryResponseIsInvalid = "915121"
+        ThreeDSecureEciFlagIsInvalid = "915114"
+        ThreeDSecureEciFlagIsRequired = "915113"
         ThreeDSecureMerchantAccountDoesNotSupportCardType = "915131"
+        ThreeDSecureTokenIsInvalid = "91568"
+        ThreeDSecureTransactionDataDoesntMatchVerify = "91570"
+        ThreeDSecureTransactionPaymentMethodDoesntMatchThreeDSecureAuthenticationPaymentMethod = "915197"
+        ThreeDSecureXidIsRequired = "915115"
         TooManyLineItems = "915157"
-        LineItemsExpected = "915158"
+        TransactionIsNotEligibleForAdjustment = "915219"
+        TransactionMustBeInStateAuthorized = "915218"
+        TransactionSourceIsInvalid = "915133"
         TypeIsInvalid = "91523"
         TypeIsRequired = "91524"
         UnsupportedVoiceAuthorization = "91539"
         UsBankAccountNonceMustBePlaidVerified = "915171"
         UsBankAccountNotVerified = "915172"
-        TransactionSourceIsInvalid = "915133"
 
         class ExternalVault(object):
             StatusIsInvalid = "915175"
             StatusWithPreviousNetworkTransactionIdIsInvalid = "915177"
+            # NEXT_MAJOR_VERSION remove this validation error as it is no longer returned by the gateway
             CardTypeIsInvalid = "915178"
             PreviousNetworkTransactionIdIsInvalid = "915179"
 
@@ -701,7 +742,6 @@ class ErrorCodes(object):
             DescriptionIsTooLong = "95803"
             DiscountAmountFormatIsInvalid = "95804"
             DiscountAmountIsTooLarge = "95805"
-            DiscountAmountMustBeGreaterThanZero = "95806" # Deprecated as the amount may be zero.
             DiscountAmountCannotBeNegative = "95806"
             KindIsInvalid = "95807"
             KindIsRequired = "95808"
@@ -722,7 +762,6 @@ class ErrorCodes(object):
             UnitOfMeasureIsTooLarge = "95821"
             UnitTaxAmountFormatIsInvalid = "95824"
             UnitTaxAmountIsTooLarge = "95825"
-            UnitTaxAmountMustBeGreaterThanZero = "95826" # Deprecated as the amount may be zero.
             UnitTaxAmountCannotBeNegative = "95826"
             TaxAmountFormatIsInvalid = "95827"
             TaxAmountIsTooLarge = "95828"
@@ -735,3 +774,12 @@ class ErrorCodes(object):
         TooManyConfirmationAttempts = "96104"
         UnableToConfirmDepositAmounts = "96105"
         InvalidDepositAmounts = "96106"
+
+    class RiskData(object):
+        # NEXT_MAJOR_VERSION Remove CustomerBrowserIsTooLong code as it is no longer used
+        CustomerBrowserIsTooLong = "94701"
+        CustomerDeviceIdIsTooLong = "94702"
+        CustomerLocationZipInvalidCharacters = "94703"
+        CustomerLocationZipIsInvalid = "94704"
+        CustomerLocationZipIsTooLong = "94705"
+        CustomerTenureIsTooLong = "94706"
diff --git a/braintree/error_result.py b/braintree/error_result.py
index 43f8cd0..c98cbfb 100644
--- a/braintree/error_result.py
+++ b/braintree/error_result.py
@@ -40,6 +40,11 @@ class ErrorResult(object):
         else:
             self.subscription = None
 
+        if "plan" in attributes:
+            self.plan = braintree.plan.Plan(gateway, attributes["plan"])
+        else:
+            self.plan = None
+
         if "merchant_account" in attributes:
             self.merchant_account = braintree.merchant_account.MerchantAccount(gateway, attributes["merchant_account"])
         else:
diff --git a/braintree/europe_bank_account.py b/braintree/europe_bank_account.py
index 7540990..331755d 100644
--- a/braintree/europe_bank_account.py
+++ b/braintree/europe_bank_account.py
@@ -2,6 +2,7 @@ import braintree
 from braintree.resource import Resource
 from braintree.configuration import Configuration
 
+#NEXT_MAJOR_VERSION this was specific to iDEAL integrations and can be removed
 class EuropeBankAccount(Resource):
     class MandateType(object):
         """
diff --git a/braintree/exceptions/__init__.py b/braintree/exceptions/__init__.py
index 1daf76d..eb9de84 100644
--- a/braintree/exceptions/__init__.py
+++ b/braintree/exceptions/__init__.py
@@ -1,13 +1,14 @@
 from braintree.exceptions.authentication_error import AuthenticationError
 from braintree.exceptions.authorization_error import AuthorizationError
-from braintree.exceptions.down_for_maintenance_error import DownForMaintenanceError
-from braintree.exceptions.forged_query_string_error import ForgedQueryStringError
+from braintree.exceptions.configuration_error import ConfigurationError
+from braintree.exceptions.gateway_timeout_error import GatewayTimeoutError
 from braintree.exceptions.invalid_challenge_error import InvalidChallengeError
 from braintree.exceptions.invalid_signature_error import InvalidSignatureError
 from braintree.exceptions.not_found_error import NotFoundError
+from braintree.exceptions.request_timeout_error import RequestTimeoutError
 from braintree.exceptions.server_error import ServerError
+from braintree.exceptions.service_unavailable_error import ServiceUnavailableError
+from braintree.exceptions.test_operation_performed_in_production_error import TestOperationPerformedInProductionError
 from braintree.exceptions.too_many_requests_error import TooManyRequestsError
 from braintree.exceptions.unexpected_error import UnexpectedError
 from braintree.exceptions.upgrade_required_error import UpgradeRequiredError
-from braintree.exceptions.test_operation_performed_in_production_error import TestOperationPerformedInProductionError
-from braintree.exceptions.configuration_error import ConfigurationError
diff --git a/braintree/exceptions/authentication_error.py b/braintree/exceptions/authentication_error.py
index 65dbd6b..b034ad0 100644
--- a/braintree/exceptions/authentication_error.py
+++ b/braintree/exceptions/authentication_error.py
@@ -4,6 +4,6 @@ class AuthenticationError(BraintreeError):
     """
     Raised when the client library cannot authenticate with the gateway.  This generally means the public_key/private key are incorrect, or the user is not active.
 
-    See https://developers.braintreepayments.com/reference/general/exceptions/python#authentication-error
+    See https://developer.paypal.com/braintree/docs/reference/general/exceptions/python#authentication-error
     """
     pass
diff --git a/braintree/exceptions/authorization_error.py b/braintree/exceptions/authorization_error.py
index 12fdeca..fcfb014 100644
--- a/braintree/exceptions/authorization_error.py
+++ b/braintree/exceptions/authorization_error.py
@@ -4,6 +4,6 @@ class AuthorizationError(BraintreeError):
     """
     Raised when the user does not have permission to complete the requested operation.
 
-    See https://developers.braintreepayments.com/reference/general/exceptions/python#authorization-error
+    See https://developer.paypal.com/braintree/docs/reference/general/exceptions/python#authorization-error
     """
     pass
diff --git a/braintree/exceptions/down_for_maintenance_error.py b/braintree/exceptions/down_for_maintenance_error.py
deleted file mode 100644
index d0aa166..0000000
--- a/braintree/exceptions/down_for_maintenance_error.py
+++ /dev/null
@@ -1,9 +0,0 @@
-from braintree.exceptions.braintree_error import BraintreeError
-
-class DownForMaintenanceError(BraintreeError):
-    """
-    Raised when the gateway is down for maintenance.
-
-    See https://developers.braintreepayments.com/reference/general/exceptions/python#down-for-maintenance
-    """
-    pass
diff --git a/braintree/exceptions/forged_query_string_error.py b/braintree/exceptions/forged_query_string_error.py
deleted file mode 100644
index 5dbd7cd..0000000
--- a/braintree/exceptions/forged_query_string_error.py
+++ /dev/null
@@ -1,9 +0,0 @@
-from braintree.exceptions.braintree_error import BraintreeError
-
-class ForgedQueryStringError(BraintreeError):
-    """
-    Raised when the query string has been forged or tampered with during a transparent redirect.
-
-    See https://developers.braintreepayments.com/reference/general/exceptions/python#forged-query-string
-    """
-    pass
diff --git a/braintree/exceptions/gateway_timeout_error.py b/braintree/exceptions/gateway_timeout_error.py
new file mode 100644
index 0000000..31d1a82
--- /dev/null
+++ b/braintree/exceptions/gateway_timeout_error.py
@@ -0,0 +1,7 @@
+from braintree.exceptions.braintree_error import BraintreeError
+
+class GatewayTimeoutError(BraintreeError):
+    """
+    Raised when a gateway response timeout occurs.
+    """
+    pass
diff --git a/braintree/exceptions/not_found_error.py b/braintree/exceptions/not_found_error.py
index e738e2b..a79eec4 100644
--- a/braintree/exceptions/not_found_error.py
+++ b/braintree/exceptions/not_found_error.py
@@ -4,6 +4,6 @@ class NotFoundError(BraintreeError):
     """
     Raised when an object is not found in the gateway, such as a Transaction.find("bad_id").
 
-    See https://developers.braintreepayments.com/reference/general/exceptions/python#not-found-error
+    See https://developer.paypal.com/braintree/docs/reference/general/exceptions/python#not-found-error
     """
     pass
diff --git a/braintree/exceptions/request_timeout_error.py b/braintree/exceptions/request_timeout_error.py
new file mode 100644
index 0000000..51fabab
--- /dev/null
+++ b/braintree/exceptions/request_timeout_error.py
@@ -0,0 +1,7 @@
+from braintree.exceptions.braintree_error import BraintreeError
+
+class RequestTimeoutError(BraintreeError):
+    """
+    Raised when a client request timeout occurs.
+    """
+    pass
diff --git a/braintree/exceptions/server_error.py b/braintree/exceptions/server_error.py
index ebbee70..751dc6a 100644
--- a/braintree/exceptions/server_error.py
+++ b/braintree/exceptions/server_error.py
@@ -2,8 +2,8 @@ from braintree.exceptions.braintree_error import BraintreeError
 
 class ServerError(BraintreeError):
     """
-    Raised when the gateway raises an error.  Please contant support at support@getbraintree.com.
+    Raised when the gateway raises an error.  Please contact support at support@getbraintree.com.
 
-    See https://developers.braintreepayments.com/reference/general/exceptions/python#server-error
+    See https://developer.paypal.com/braintree/docs/reference/general/exceptions/python#server-error
     """
     pass
diff --git a/braintree/exceptions/service_unavailable_error.py b/braintree/exceptions/service_unavailable_error.py
new file mode 100644
index 0000000..8043fd8
--- /dev/null
+++ b/braintree/exceptions/service_unavailable_error.py
@@ -0,0 +1,7 @@
+from braintree.exceptions.braintree_error import BraintreeError
+
+class ServiceUnavailableError(BraintreeError):
+    """
+    Raised when the gateway is unavailable.
+    """
+    pass
diff --git a/braintree/exceptions/upgrade_required_error.py b/braintree/exceptions/upgrade_required_error.py
index 8f40c5f..42f9f9f 100644
--- a/braintree/exceptions/upgrade_required_error.py
+++ b/braintree/exceptions/upgrade_required_error.py
@@ -4,6 +4,6 @@ class UpgradeRequiredError(BraintreeError):
     """
     Raised for unsupported client library versions.
 
-    See https://developers.braintreepayments.com/reference/general/exceptions/python#upgrade-required-error
+    See https://developer.paypal.com/braintree/docs/reference/general/exceptions/python#upgrade-required-error
     """
     pass
diff --git a/braintree/exchange_rate_quote.py b/braintree/exchange_rate_quote.py
new file mode 100644
index 0000000..ba1565f
--- /dev/null
+++ b/braintree/exchange_rate_quote.py
@@ -0,0 +1,6 @@
+from braintree.attribute_getter import AttributeGetter
+from braintree.montary_amount import MontaryAmount
+
+class ExchangeRateQuote(AttributeGetter):
+    def __init__(self,attributes):
+        AttributeGetter.__init__(self,attributes)
\ No newline at end of file
diff --git a/braintree/exchange_rate_quote_gateway.py b/braintree/exchange_rate_quote_gateway.py
new file mode 100644
index 0000000..6ad6cec
--- /dev/null
+++ b/braintree/exchange_rate_quote_gateway.py
@@ -0,0 +1,38 @@
+from braintree.exchange_rate_quote_payload import ExchangeRateQuotePayload
+from braintree.error_result import ErrorResult
+from braintree.successful_result import SuccessfulResult
+
+class ExchangeRateQuoteGateway(object):
+    def __init__(self, gateway, graphql_client = None):
+        self.gateway = gateway
+        self.config = gateway.config
+        self.graphql_client = None if graphql_client is None else graphql_client
+      
+    def generate(self, request):
+        definition = """
+          mutation ($exchangeRateQuoteRequest: GenerateExchangeRateQuoteInput!) {
+            generateExchangeRateQuote(input: $exchangeRateQuoteRequest) {
+              quotes {
+                id
+                baseAmount {value, currencyCode}
+                quoteAmount {value, currencyCode}
+                exchangeRate
+                tradeRate
+                expiresAt
+                refreshesAt
+              }
+            }
+          }"""
+
+        param = request.to_graphql_variables()
+        graphql_client = self.graphql_client if self.graphql_client is not None else self.gateway.graphql_client
+        response = graphql_client.query(definition, param)
+
+        if "data" in response and "generateExchangeRateQuote" in response["data"]:
+            result = response["data"]["generateExchangeRateQuote"]
+            self.exchange_rate_quote_payload = ExchangeRateQuotePayload(result)
+            return SuccessfulResult({"exchange_rate_quote_payload": self.exchange_rate_quote_payload})
+        elif "errors" in response:
+            error_codes = response["errors"][0]
+            error_codes["errors"] = dict()
+            return ErrorResult(self.gateway, error_codes)
\ No newline at end of file
diff --git a/braintree/exchange_rate_quote_input.py b/braintree/exchange_rate_quote_input.py
new file mode 100644
index 0000000..197d9c8
--- /dev/null
+++ b/braintree/exchange_rate_quote_input.py
@@ -0,0 +1,17 @@
+from braintree.attribute_getter import AttributeGetter
+
+class ExchangeRateQuoteInput(AttributeGetter):
+    def __init__(self,parent,attributes):
+        self.parent = parent
+        AttributeGetter.__init__(self,attributes)
+
+    def done(self):
+        return self.parent
+
+    def to_graphql_variables(self):
+        variables = dict()
+        variables["baseCurrency"] = self.base_currency if getattr(self,"base_currency",None) is not None else None
+        variables["quoteCurrency"] = self.quote_currency if getattr(self,"quote_currency",None) is not None else None
+        variables["baseAmount"] = self.base_amount if getattr(self,"base_amount",None) is not None else None
+        variables["markup"] = self.markup if getattr(self,"markup",None) is not None else None
+        return variables
\ No newline at end of file
diff --git a/braintree/exchange_rate_quote_payload.py b/braintree/exchange_rate_quote_payload.py
new file mode 100644
index 0000000..2107859
--- /dev/null
+++ b/braintree/exchange_rate_quote_payload.py
@@ -0,0 +1,29 @@
+from braintree.exchange_rate_quote import ExchangeRateQuote
+from braintree.montary_amount import MontaryAmount
+
+class ExchangeRateQuotePayload(object):
+    def __init__(self, data):
+        quote_objs = data.get("quotes")
+        if(quote_objs is not None):
+            self.quotes = list()
+            for quote_obj in quote_objs:
+                base_amount_obj = quote_obj.get("baseAmount")
+                quote_amount_obj = quote_obj.get("quoteAmount")
+                base_attrs = {"value":base_amount_obj.get("value"),
+                              "currency_code":base_amount_obj.get("currencyCode")}
+                base_amount = MontaryAmount(base_attrs)
+                quote_attrs = {"value":quote_amount_obj.get("value"),
+                               "currency_code":quote_amount_obj.get("currencyCode")}
+                quote_amount = MontaryAmount(quote_attrs)
+                attributes = {"id":quote_obj.get("id"),
+                              "exchange_rate":quote_obj.get("exchangeRate"),
+                              "trade_rate":quote_obj.get("tradeRate"),
+                              "expires_at":quote_obj.get("expiresAt"),
+                              "refreshes_at":quote_obj.get("refreshesAt"),
+                              "base_amount":base_amount,
+                              "quote_amount":quote_amount}
+                quote = ExchangeRateQuote(attributes)
+                self.quotes.append(quote)
+
+    def get_quotes(self):
+        return self.quotes
\ No newline at end of file
diff --git a/braintree/exchange_rate_quote_request.py b/braintree/exchange_rate_quote_request.py
new file mode 100644
index 0000000..d0e1061
--- /dev/null
+++ b/braintree/exchange_rate_quote_request.py
@@ -0,0 +1,21 @@
+from braintree.exchange_rate_quote_input import ExchangeRateQuoteInput
+
+class ExchangeRateQuoteRequest(object):
+    def __init__(self):
+        self.quotes = list()
+
+    def add_exchange_rate_quote_input(self,attributes):
+        new_input = ExchangeRateQuoteInput(self,attributes)
+        self.quotes.append(new_input)
+        return new_input
+
+    def to_graphql_variables(self):
+        variables = dict()
+        input = dict()
+        
+        quote_list = list()
+        for quote in self.quotes:
+            quote_list.append(quote.to_graphql_variables())
+        input["quotes"] = quote_list
+        variables["exchangeRateQuoteRequest"] = input
+        return variables
\ No newline at end of file
diff --git a/braintree/ideal_payment.py b/braintree/ideal_payment.py
deleted file mode 100644
index 627dcf7..0000000
--- a/braintree/ideal_payment.py
+++ /dev/null
@@ -1,28 +0,0 @@
-import braintree
-from braintree.resource import Resource
-from braintree.configuration import Configuration
-from braintree.iban_bank_account import IbanBankAccount
-
-# NEXT_MAJOR_VERSION Remove this class as legacy Ideal has been removed/disabled in the Braintree Gateway
-# DEPRECATED If you're looking to accept iDEAL as a payment method contact accounts@braintreepayments.com for a solution.
-class IdealPayment(Resource):
-
-    @staticmethod
-    def find(ideal_payment_id):
-        return Configuration.gateway().ideal_payment.find(ideal_payment_id)
-
-    @staticmethod
-    def sale(ideal_payment_id, transactionRequest):
-        request = transactionRequest.copy()
-        request["payment_method_nonce"] = ideal_payment_id
-        if not "options" in request:
-            request["options"] = {}
-        request["options"]["submit_for_settlement"] = True
-        return Configuration.gateway().transaction.sale(request)
-
-    def __init__(self, gateway, attributes):
-        Resource.__init__(self, gateway, attributes)
-        if attributes.get('iban_bank_account') is not None:
-            self.iban_bank_account = IbanBankAccount(gateway, self.iban_bank_account)
-        else:
-            self.iban_bank_account = None
diff --git a/braintree/ideal_payment_gateway.py b/braintree/ideal_payment_gateway.py
deleted file mode 100644
index 46df0cb..0000000
--- a/braintree/ideal_payment_gateway.py
+++ /dev/null
@@ -1,21 +0,0 @@
-import braintree
-from braintree.ideal_payment import IdealPayment
-from braintree.exceptions.not_found_error import NotFoundError
-
-# NEXT_MAJOR_VERSION Remove this class as legacy Ideal has been removed/disabled in the Braintree Gateway
-# DEPRECATED If you're looking to accept iDEAL as a payment method contact accounts@braintreepayments.com for a solution.
-class IdealPaymentGateway(object):
-    def __init__(self, gateway):
-        self.gateway = gateway
-        self.config = gateway.config
-
-    def find(self, ideal_payment_id):
-        try:
-            if ideal_payment_id is None or ideal_payment_id.strip() == "":
-                raise NotFoundError()
-
-            response = self.config.http().get(self.config.base_merchant_path() + "/ideal_payments/" + ideal_payment_id)
-            if "ideal_payment" in response:
-                return IdealPayment(self.gateway, response["ideal_payment"])
-        except NotFoundError:
-            raise NotFoundError("iDEAL payment with token" + repr(ideal_payment_id) + " not found")
diff --git a/braintree/liability_shift.py b/braintree/liability_shift.py
new file mode 100644
index 0000000..8e5526c
--- /dev/null
+++ b/braintree/liability_shift.py
@@ -0,0 +1,4 @@
+from braintree.attribute_getter import AttributeGetter
+
+class LiabilityShift(AttributeGetter):
+    pass
diff --git a/braintree/local_payment_expired.py b/braintree/local_payment_expired.py
new file mode 100644
index 0000000..9638559
--- /dev/null
+++ b/braintree/local_payment_expired.py
@@ -0,0 +1,9 @@
+from braintree.resource import Resource
+
+class LocalPaymentExpired(Resource):
+    """
+    A class representing Braintree LocalPaymentExpired webhook.
+    """
+    def __init__(self, gateway, attributes):
+        Resource.__init__(self, gateway, attributes)
+
diff --git a/braintree/local_payment_funded.py b/braintree/local_payment_funded.py
new file mode 100644
index 0000000..b59d6a4
--- /dev/null
+++ b/braintree/local_payment_funded.py
@@ -0,0 +1,11 @@
+from braintree.resource import Resource
+from braintree.transaction import Transaction
+
+class LocalPaymentFunded(Resource):
+    """
+    A class representing Braintree LocalPaymentFunded webhook.
+    """
+    def __init__(self, gateway, attributes):
+        Resource.__init__(self, gateway, attributes)
+
+        self.transaction = Transaction(gateway, attributes.pop("transaction"))
diff --git a/braintree/local_payment_reversed.py b/braintree/local_payment_reversed.py
new file mode 100644
index 0000000..636aa38
--- /dev/null
+++ b/braintree/local_payment_reversed.py
@@ -0,0 +1,9 @@
+from braintree.resource import Resource
+
+class LocalPaymentReversed(Resource):
+    """
+    A class representing Braintree LocalPaymentReversed webhook.
+    """
+    def __init__(self, gateway, attributes):
+        Resource.__init__(self, gateway, attributes)
+
diff --git a/braintree/masterpass_card.py b/braintree/masterpass_card.py
index 5350cf6..34198c2 100644
--- a/braintree/masterpass_card.py
+++ b/braintree/masterpass_card.py
@@ -1,12 +1,14 @@
 import braintree
 from braintree.address import Address
 from braintree.resource import Resource
+from warnings import warn
 
 class MasterpassCard(Resource):
     """
-    A class representing Masterpass card
+    A class representing Masterpass card. Deprecated
     """
     def __init__(self, gateway, attributes):
+        warn("MasterpassCard is deprecated")
         Resource.__init__(self, gateway, attributes)
 
         if "billing_address" in attributes:
diff --git a/braintree/merchant_account/merchant_account.py b/braintree/merchant_account/merchant_account.py
index df8178f..13f81cf 100644
--- a/braintree/merchant_account/merchant_account.py
+++ b/braintree/merchant_account/merchant_account.py
@@ -2,6 +2,7 @@ from braintree.configuration import Configuration
 from braintree.resource import Resource
 from braintree.merchant_account import BusinessDetails, FundingDetails, IndividualDetails
 
+
 class MerchantAccount(Resource):
     class Status(object):
         Active = "active"
@@ -37,7 +38,9 @@ class MerchantAccount(Resource):
         return super(MerchantAccount, self).__repr__(detail_list)
 
     @staticmethod
-    def create(params={}):
+    def create(params=None):
+        if params is None:
+            params = {}
         return Configuration.gateway().merchant_account.create(params)
 
     @staticmethod
diff --git a/braintree/merchant_account_gateway.py b/braintree/merchant_account_gateway.py
index f0454a8..6e52db9 100644
--- a/braintree/merchant_account_gateway.py
+++ b/braintree/merchant_account_gateway.py
@@ -7,16 +7,21 @@ from braintree.resource_collection import ResourceCollection
 from braintree.successful_result import SuccessfulResult
 from braintree.exceptions.not_found_error import NotFoundError
 
+
 class MerchantAccountGateway(object):
     def __init__(self, gateway):
         self.gateway = gateway
         self.config = gateway.config
 
-    def create(self, params={}):
-        Resource.verify_keys(params, MerchantAccountGateway._detect_signature(params))
+    def create(self, params=None):
+        if params is None:
+            params = {}
+        Resource.verify_keys(params, MerchantAccountGateway._create_signature())
         return self._post("/merchant_accounts/create_via_api", {"merchant_account": params})
 
-    def update(self, merchant_account_id, params={}):
+    def update(self, merchant_account_id, params=None):
+        if params is None:
+            params = {}
         Resource.verify_keys(params, MerchantAccountGateway._update_signature())
         return self._put("/merchant_accounts/%s/update_via_api" % merchant_account_id, {"merchant_account": params})
 
@@ -29,7 +34,9 @@ class MerchantAccountGateway(object):
         except NotFoundError:
             raise NotFoundError("merchant account with id " + repr(merchant_account_id) + " not found")
 
-    def create_for_currency(self, params={}):
+    def create_for_currency(self, params=None):
+        if params is None:
+            params = {}
         return self._post("/merchant_accounts/create_for_currency", {"merchant_account": params})
 
     def all(self):
@@ -42,7 +49,9 @@ class MerchantAccountGateway(object):
         merchant_accounts = [MerchantAccount(self.gateway, merchant_account) for merchant_account in ResourceCollection._extract_as_array(body, "merchant_account")]
         return PaginatedResult(body["total_items"], body["page_size"], merchant_accounts)
 
-    def _post(self, url, params={}):
+    def _post(self, url, params=None):
+        if params is None:
+            params = {}
         response = self.config.http().post(self.config.base_merchant_path() + url, params)
 
         if "response" in response:
@@ -53,47 +62,15 @@ class MerchantAccountGateway(object):
         elif "api_error_response" in response:
             return ErrorResult(self.gateway, response["api_error_response"])
 
-    def _put(self, url, params={}):
+    def _put(self, url, params=None):
+        if params is None:
+            params = {}
         response = self.config.http().put(self.config.base_merchant_path() + url, params)
         if "merchant_account" in response:
             return SuccessfulResult({"merchant_account": MerchantAccount(self.gateway, response["merchant_account"])})
         elif "api_error_response" in response:
             return ErrorResult(self.gateway, response["api_error_response"])
 
-    @staticmethod
-    def _detect_signature(attributes):
-        if 'applicant_details' in attributes:
-            # Warn deprecated
-            return MerchantAccountGateway._create_deprecated_signature()
-        else:
-            return MerchantAccountGateway._create_signature()
-
-    @staticmethod
-    def _create_deprecated_signature():
-        return [
-            {'applicant_details': [
-                'company_name',
-                'first_name',
-                'last_name',
-                'email',
-                'phone',
-                'date_of_birth',
-                'ssn',
-                'tax_id',
-                'routing_number',
-                'account_number',
-                {'address': [
-                    'street_address',
-                    'postal_code',
-                    'locality',
-                    'region']}
-                ]
-            },
-            'tos_accepted',
-            'master_merchant_account_id',
-            'id'
-        ]
-
     @staticmethod
     def _create_signature():
         return [
diff --git a/braintree/merchant_gateway.py b/braintree/merchant_gateway.py
index 581ad58..66d0b15 100644
--- a/braintree/merchant_gateway.py
+++ b/braintree/merchant_gateway.py
@@ -6,6 +6,7 @@ from braintree.exceptions.not_found_error import NotFoundError
 from braintree.merchant import Merchant
 from braintree.oauth_credentials import OAuthCredentials
 
+
 class MerchantGateway(object):
     def __init__(self, gateway):
         self.gateway = gateway
@@ -14,7 +15,9 @@ class MerchantGateway(object):
     def create(self, params):
         return self.__create_merchant(params)
 
-    def __create_merchant(self, params={}):
+    def __create_merchant(self, params=None):
+        if params is None:
+            params = {}
         response = self.config.http().post("/merchants/create_via_api", {
             "merchant": params
         })
diff --git a/braintree/montary_amount.py b/braintree/montary_amount.py
new file mode 100644
index 0000000..aecbcff
--- /dev/null
+++ b/braintree/montary_amount.py
@@ -0,0 +1,8 @@
+from decimal import Decimal
+from braintree.attribute_getter import AttributeGetter
+
+class MontaryAmount(AttributeGetter):
+    def __init__(self,attributes):
+        AttributeGetter.__init__(self,attributes)
+        if getattr(self, "value", None) is not None:
+            self.value = Decimal(self.value)
\ No newline at end of file
diff --git a/braintree/oauth_gateway.py b/braintree/oauth_gateway.py
index b926825..5b6b25c 100644
--- a/braintree/oauth_gateway.py
+++ b/braintree/oauth_gateway.py
@@ -5,11 +5,8 @@ from braintree.exceptions.not_found_error import NotFoundError
 from braintree.oauth_credentials import OAuthCredentials
 
 import sys
-if sys.version_info[0] == 2:
-    from urllib import quote_plus
-else:
-    from urllib.parse import quote_plus
-    from functools import reduce
+from urllib.parse import quote_plus
+from functools import reduce
 
 class OAuthGateway(object):
     def __init__(self, gateway):
diff --git a/braintree/payment_instrument_type.py b/braintree/payment_instrument_type.py
index c9ce632..3ca33e3 100644
--- a/braintree/payment_instrument_type.py
+++ b/braintree/payment_instrument_type.py
@@ -1,16 +1,18 @@
 
 class PaymentInstrumentType():
+    # NEXT_MAJOR_VERSION remove amex express checkout
+    # NEXT_MAJOR_VERSION remove masterpass
+    AmexExpressCheckoutCard = "amex_express_checkout_card"
+    AndroidPayCard = "android_pay_card"
+    ApplePayCard = "apple_pay_card"
+    CreditCard = "credit_card"
+    EuropeBankAccount = "europe_bank_account"
+    LocalPayment = "local_payment"
+    MasterpassCard = "masterpass_card"
     PayPalAccount = "paypal_account"
     PayPalHere = "paypal_here"
-    EuropeBankAccount = "europe_bank_account"
-    CreditCard = "credit_card"
-    CoinbaseAccount = "coinbase_account"
-    ApplePayCard = "apple_pay_card"
-    AndroidPayCard = "android_pay_card"
-    AmexExpressCheckoutCard = "amex_express_checkout_card"
-    VenmoAccount = "venmo_account"
+    SamsungPayCard = "samsung_pay_card"
+    SepaDirectDebitAccount = "sepa_debit_account"
     UsBankAccount = "us_bank_account"
+    VenmoAccount = "venmo_account"
     VisaCheckoutCard = "visa_checkout_card"
-    MasterpassCard = "masterpass_card"
-    SamsungPayCard = "samsung_pay_card"
-    LocalPayment = "local_payment"
diff --git a/braintree/payment_method.py b/braintree/payment_method.py
index 02920ff..d1cb02a 100644
--- a/braintree/payment_method.py
+++ b/braintree/payment_method.py
@@ -3,9 +3,12 @@ from braintree.address import Address
 from braintree.resource import Resource
 from braintree.configuration import Configuration
 
+
 class PaymentMethod(Resource):
     @staticmethod
-    def create(params={}):
+    def create(params=None):
+        if params is None:
+            params = {}
         return Configuration.gateway().payment_method.create(params)
 
     @staticmethod
@@ -17,7 +20,9 @@ class PaymentMethod(Resource):
         return Configuration.gateway().payment_method.update(payment_method_token, params)
 
     @staticmethod
-    def delete(payment_method_token, options={}):
+    def delete(payment_method_token, options=None):
+        if options is None:
+            options = {}
         return Configuration.gateway().payment_method.delete(payment_method_token, options)
 
     @staticmethod
@@ -29,11 +34,12 @@ class PaymentMethod(Resource):
         options = [
             "fail_on_duplicate_payment_method",
             "make_default",
+            "skip_advanced_fraud_checking",
             "us_bank_account_verification_method",
+            "verification_account_type",
+            "verification_amount",
             "verification_merchant_account_id",
             "verify_card",
-            "verification_amount",
-            "verification_account_type",
             {
                 "adyen": [
                     "overwrite_brand",
@@ -52,55 +58,74 @@ class PaymentMethod(Resource):
             },
         ]
 
+        three_d_secure_pass_thru = [
+            "cavv",
+            "ds_transaction_id",
+            "eci_flag",
+            "three_d_secure_version",
+            "xid"
+        ]
+
         signature = [
             "billing_address_id",
             "cardholder_name",
             "customer_id",
             "cvv",
             "device_data",
-            "device_session_id",
             "expiration_date",
             "expiration_month",
             "expiration_year",
             "number",
             "payment_method_nonce",
             "paypal_refresh_token",
-            "paypal_vault_without_upgrade",
             "token",
+            "device_session_id", # NEXT_MAJOR_VERSION remove device_session_id
             {
                 "billing_address": Address.create_signature()
             },
             {
                 "options": options
+            },
+            {
+                "three_d_secure_pass_thru": three_d_secure_pass_thru
             }
+
         ]
         return signature
 
     @staticmethod
     def update_signature():
+        three_d_secure_pass_thru = [
+            "cavv",
+            "ds_transaction_id",
+            "eci_flag",
+            "three_d_secure_version",
+            "xid"
+        ]
+
         signature = [
             "billing_address_id",
             "cardholder_name",
             "cvv",
-            "device_session_id",
+            "device_data",
             "expiration_date",
             "expiration_month",
             "expiration_year",
             "number",
+            "payment_method_nonce",
             "token",
             "venmo_sdk_payment_method_code",
-            "device_data",
-            "fraud_merchant_id",
-            "payment_method_nonce",
+            "device_session_id", "fraud_merchant_id", # NEXT_MAJOR_VERSION remove device_session_id and fraud_merchant_id
             {
                 "options": [
                     "make_default",
+                    "skip_advanced_fraud_checking",
                     "us_bank_account_verification_method",
-                    "verify_card",
+                    "venmo_sdk_session",
+                    "verification_account_type",
                     "verification_amount",
                     "verification_merchant_account_id",
-                    "verification_account_type",
-                    "venmo_sdk_session",
+                    "verify_card",
                     {
                         "adyen": [
                             "overwrite_brand",
@@ -111,6 +136,9 @@ class PaymentMethod(Resource):
             },
             {
                 "billing_address": Address.update_signature() + [{"options": ["update_existing"]}]
+            },
+            {
+                "three_d_secure_pass_thru": three_d_secure_pass_thru
             }
         ]
         return signature
diff --git a/braintree/payment_method_customer_data_updated_metadata.py b/braintree/payment_method_customer_data_updated_metadata.py
new file mode 100644
index 0000000..e1e3a02
--- /dev/null
+++ b/braintree/payment_method_customer_data_updated_metadata.py
@@ -0,0 +1,14 @@
+from braintree.resource import Resource
+from braintree.payment_method_parser import parse_payment_method
+from braintree.enriched_customer_data import EnrichedCustomerData
+
+class PaymentMethodCustomerDataUpdatedMetadata(Resource):
+    """
+    A class representing Braintree PaymentMethodCustomerDataUpdatedMetadata webhook.
+    """
+    def __init__(self, gateway, attributes):
+        Resource.__init__(self, gateway, attributes)
+        self.payment_method = parse_payment_method(gateway, attributes["payment_method"])
+        if attributes["enriched_customer_data"]:
+            self.enriched_customer_data = EnrichedCustomerData(gateway, attributes["enriched_customer_data"])
+
diff --git a/braintree/payment_method_gateway.py b/braintree/payment_method_gateway.py
index 954cd57..5cd475c 100644
--- a/braintree/payment_method_gateway.py
+++ b/braintree/payment_method_gateway.py
@@ -4,12 +4,14 @@ from braintree.credit_card import CreditCard
 from braintree.payment_method import PaymentMethod
 from braintree.paypal_account import PayPalAccount
 from braintree.europe_bank_account import EuropeBankAccount
-from braintree.coinbase_account import CoinbaseAccount
 from braintree.android_pay_card import AndroidPayCard
+# NEXT_MAJOR_VERSION remove amex express checkout
 from braintree.amex_express_checkout_card import AmexExpressCheckoutCard
+from braintree.sepa_direct_debit_account import SepaDirectDebitAccount
 from braintree.venmo_account import VenmoAccount
 from braintree.us_bank_account import UsBankAccount
 from braintree.visa_checkout_card import VisaCheckoutCard
+# NEXT_MAJOR_VERSION remove masterpass
 from braintree.masterpass_card import MasterpassCard
 from braintree.samsung_pay_card import SamsungPayCard
 from braintree.unknown_payment_method import UnknownPaymentMethod
@@ -23,18 +25,19 @@ from braintree.resource_collection import ResourceCollection
 from braintree.successful_result import SuccessfulResult
 
 import sys
-if sys.version_info[0] == 2:
-    from urllib import urlencode
-else:
-    from urllib.parse import urlencode
+from urllib.parse import urlencode
+
 
 class PaymentMethodGateway(object):
     def __init__(self, gateway):
         self.gateway = gateway
         self.config = gateway.config
 
-    def create(self, params={}):
+    def create(self, params=None):
+        if params is None:
+            params = {}
         Resource.verify_keys(params, PaymentMethod.create_signature())
+        self.__check_for_deprecated_attributes(params);
         return self._post("/payment_methods", {"payment_method": params})
 
     def find(self, payment_method_token):
@@ -49,6 +52,7 @@ class PaymentMethodGateway(object):
 
     def update(self, payment_method_token, params):
         Resource.verify_keys(params, PaymentMethod.update_signature())
+        self.__check_for_deprecated_attributes(params);
         try:
             if payment_method_token is None or payment_method_token.strip() == "":
                 raise NotFoundError()
@@ -60,7 +64,9 @@ class PaymentMethodGateway(object):
         except NotFoundError:
             raise NotFoundError("payment method with token " + repr(payment_method_token) + " not found")
 
-    def delete(self, payment_method_token, options={}):
+    def delete(self, payment_method_token, options=None):
+        if options is None:
+            options = {}
         Resource.verify_keys(options, PaymentMethod.delete_signature())
         query_param = ""
         if options:
@@ -114,13 +120,15 @@ class PaymentMethodGateway(object):
         except NotFoundError:
             raise NotFoundError("payment method with payment_method_token " + repr(payment_method_token) + " not found")
 
-    def _post(self, url, params={}, result_key="payment_method"):
+    def _post(self, url, params=None, result_key="payment_method"):
+        if params is None:
+            params = {}
         response = self.config.http().post(self.config.base_merchant_path() + url, params)
         if "api_error_response" in response:
             return ErrorResult(self.gateway, response["api_error_response"])
-        elif result_key is "revoke" and response.get("success", False):
+        elif result_key == "revoke" and response.get("success", False):
             return SuccessfulResult()
-        elif result_key is "payment_method_nonce":
+        elif result_key == "payment_method_nonce":
             payment_method_nonce = self._parse_payment_method_nonce(response)
             return SuccessfulResult({result_key: payment_method_nonce})
         else:
@@ -128,7 +136,9 @@ class PaymentMethodGateway(object):
             return SuccessfulResult({result_key: payment_method})
         return response
 
-    def _put(self, url, params={}):
+    def _put(self, url, params=None):
+        if params is None:
+            params = {}
         response = self.config.http().put(self.config.base_merchant_path() + url, params)
         if "api_error_response" in response:
             return ErrorResult(self.gateway, response["api_error_response"])
@@ -140,3 +150,9 @@ class PaymentMethodGateway(object):
         if "payment_method_nonce" in response:
             return PaymentMethodNonce(self.gateway, response["payment_method_nonce"])
         raise ValueError("payment_method_nonce not present in response")
+
+    def __check_for_deprecated_attributes(self, params):
+        if "device_session_id" in params.keys():
+            warnings.warn("device_session_id is deprecated, use device_data parameter instead", DeprecationWarning)
+        if "fraud_merchant_id" in params.keys():
+            warnings.warn("fraud_merchant_id is deprecated, use device_data parameter instead", DeprecationWarning)
diff --git a/braintree/payment_method_nonce.py b/braintree/payment_method_nonce.py
index 59c5088..9402999 100644
--- a/braintree/payment_method_nonce.py
+++ b/braintree/payment_method_nonce.py
@@ -6,8 +6,8 @@ from braintree.bin_data import BinData
 
 class PaymentMethodNonce(Resource):
     @staticmethod
-    def create(payment_method_token):
-        return Configuration.gateway().payment_method_nonce.create(payment_method_token)
+    def create(payment_method_token, params = {}):
+        return Configuration.gateway().payment_method_nonce.create(payment_method_token, params)
 
     @staticmethod
     def find(payment_method_nonce):
@@ -21,6 +21,11 @@ class PaymentMethodNonce(Resource):
         else:
             self.three_d_secure_info = None
 
+        if "authentication_insight" in attributes and not attributes["authentication_insight"] is None:
+            self.authentication_insight = attributes["authentication_insight"]
+        else:
+            self.authentication_insight = None
+
         if "bin_data" in attributes and not attributes["bin_data"] is None:
             self.bin_data = BinData(attributes["bin_data"])
         else:
diff --git a/braintree/payment_method_nonce_gateway.py b/braintree/payment_method_nonce_gateway.py
index 323409b..be0e0fe 100644
--- a/braintree/payment_method_nonce_gateway.py
+++ b/braintree/payment_method_nonce_gateway.py
@@ -12,9 +12,11 @@ class PaymentMethodNonceGateway(object):
         self.gateway = gateway
         self.config = gateway.config
 
-    def create(self, payment_method_token):
+    def create(self, payment_method_token, params = {"payment_method_nonce": {}}):
         try:
-            response = self.config.http().post(self.config.base_merchant_path() + "/payment_methods/" + payment_method_token + "/nonces")
+            schema = [{"payment_method_nonce": ["merchant_account_id", "authentication_insight", {"authentication_insight_options": ["amount", "recurring_customer_consent", "recurring_max_amount"]}]}]
+            Resource.verify_keys(params, schema)
+            response = self.config.http().post(self.config.base_merchant_path() + "/payment_methods/" + payment_method_token + "/nonces", params)
             if "api_error_response" in response:
                 return ErrorResult(self.gateway, response["api_error_response"])
             else:
diff --git a/braintree/payment_method_parser.py b/braintree/payment_method_parser.py
index b7192a8..732285d 100644
--- a/braintree/payment_method_parser.py
+++ b/braintree/payment_method_parser.py
@@ -4,13 +4,13 @@ from braintree.credit_card import CreditCard
 from braintree.payment_method import PaymentMethod
 from braintree.paypal_account import PayPalAccount
 from braintree.europe_bank_account import EuropeBankAccount
-from braintree.coinbase_account import CoinbaseAccount
 from braintree.android_pay_card import AndroidPayCard
 from braintree.amex_express_checkout_card import AmexExpressCheckoutCard
 from braintree.venmo_account import VenmoAccount
 from braintree.us_bank_account import UsBankAccount
 from braintree.visa_checkout_card import VisaCheckoutCard
 from braintree.masterpass_card import MasterpassCard
+from braintree.sepa_direct_debit_account import SepaDirectDebitAccount
 from braintree.samsung_pay_card import SamsungPayCard
 from braintree.unknown_payment_method import UnknownPaymentMethod
 
@@ -25,16 +25,18 @@ def parse_payment_method(gateway, attributes):
         return ApplePayCard(gateway, attributes["apple_pay_card"])
     elif "android_pay_card" in attributes:
         return AndroidPayCard(gateway, attributes["android_pay_card"])
+    # NEXT_MAJOR_VERSION remove amex express checkout
     elif "amex_express_checkout_card" in attributes:
         return AmexExpressCheckoutCard(gateway, attributes["amex_express_checkout_card"])
-    elif "coinbase_account" in attributes:
-        return CoinbaseAccount(gateway, attributes["coinbase_account"])
+    elif "sepa_debit_account" in attributes:
+        return SepaDirectDebitAccount(gateway, attributes["sepa_debit_account"])
     elif "venmo_account" in attributes:
         return VenmoAccount(gateway, attributes["venmo_account"])
     elif "us_bank_account" in attributes:
         return UsBankAccount(gateway, attributes["us_bank_account"])
     elif "visa_checkout_card" in attributes:
         return VisaCheckoutCard(gateway, attributes["visa_checkout_card"])
+    # NEXT_MAJOR_VERSION remove masterpass
     elif "masterpass_card" in attributes:
         return MasterpassCard(gateway, attributes["masterpass_card"])
     elif "samsung_pay_card" in attributes:
diff --git a/braintree/paypal_account.py b/braintree/paypal_account.py
index 864fdde..bb08234 100644
--- a/braintree/paypal_account.py
+++ b/braintree/paypal_account.py
@@ -2,6 +2,7 @@ import braintree
 from braintree.resource import Resource
 from braintree.configuration import Configuration
 
+
 class PayPalAccount(Resource):
     @staticmethod
     def find(paypal_account_token):
@@ -12,7 +13,9 @@ class PayPalAccount(Resource):
         return Configuration.gateway().paypal_account.delete(paypal_account_token)
 
     @staticmethod
-    def update(paypal_account_token, params={}):
+    def update(paypal_account_token, params=None):
+        if params is None:
+            params = {}
         return Configuration.gateway().paypal_account.update(paypal_account_token, params)
 
     @staticmethod
diff --git a/braintree/paypal_account_gateway.py b/braintree/paypal_account_gateway.py
index c4e8a57..cd748af 100644
--- a/braintree/paypal_account_gateway.py
+++ b/braintree/paypal_account_gateway.py
@@ -5,6 +5,7 @@ from braintree.exceptions.not_found_error import NotFoundError
 from braintree.resource import Resource
 from braintree.successful_result import SuccessfulResult
 
+
 class PayPalAccountGateway(object):
     def __init__(self, gateway):
         self.gateway = gateway
@@ -25,7 +26,9 @@ class PayPalAccountGateway(object):
         self.config.http().delete(self.config.base_merchant_path() + "/payment_methods/paypal_account/" + paypal_account_token)
         return SuccessfulResult()
 
-    def update(self, paypal_account_token, params={}):
+    def update(self, paypal_account_token, params=None):
+        if params is None:
+            params = {}
         Resource.verify_keys(params, PayPalAccount.signature())
         response = self.config.http().put(self.config.base_merchant_path() + "/payment_methods/paypal_account/" + paypal_account_token, {"paypal_account": params})
         if "paypal_account" in response:
diff --git a/braintree/plan.py b/braintree/plan.py
index 01a59e3..5366f69 100644
--- a/braintree/plan.py
+++ b/braintree/plan.py
@@ -5,6 +5,8 @@ from braintree.configuration import Configuration
 from braintree.discount import Discount
 from braintree.resource_collection import ResourceCollection
 from braintree.resource import Resource
+from braintree.successful_result import SuccessfulResult
+from braintree.error_result import ErrorResult
 
 class Plan(Resource):
 
@@ -19,3 +21,72 @@ class Plan(Resource):
     def all():
         return Configuration.gateway().plan.all()
 
+    @staticmethod
+    def create(params=None):
+        if params is None:
+            params = {}
+        return Configuration.gateway().plan.create(params)
+
+    @staticmethod
+    def find(subscription_id):
+        return Configuration.gateway().plan.find(subscription_id)
+
+    @staticmethod
+    def update(subscription_id, params=None):
+        if params is None:
+            params = {}
+        return Configuration.gateway().plan.update(subscription_id, params)
+
+    @staticmethod
+    def create_signature():
+        return [
+            "billing_day_of_month",
+            "billing_frequency",
+            "currency_iso_code",
+            "description",
+            "id",
+            "merchant_id",
+            "name",
+            "number_of_billing_cycles",
+            "price",
+            "trial_duration",
+            "trial_duration_unit",
+            "trial_period"
+        ] + Plan._add_on_discount_signature()
+
+    @staticmethod
+    def update_signature():
+        return [
+            "billing_day_of_month",
+            "billing_frequency",
+            "currency_iso_code",
+            "description",
+            "id",
+            "merchant_id",
+            "name",
+            "number_of_billing_cycles",
+            "price",
+            "trial_duration",
+            "trial_duration_unit",
+            "trial_period"
+        ] + Plan._add_on_discount_signature()
+
+    @staticmethod
+    def _add_on_discount_signature():
+        return [
+            {
+                "add_ons": [
+                    {"add": ["amount", "inherited_from_id", "never_expires", "number_of_billing_cycles", "quantity"]},
+                    {"update": ["amount", "existing_id", "never_expires", "number_of_billing_cycles", "quantity"]},
+                    {"remove": ["_any_key_"]}
+                ]
+            },
+            {
+                "discounts": [
+                    {"add": ["amount", "inherited_from_id", "never_expires", "number_of_billing_cycles", "quantity"]},
+                    {"update": ["amount", "existing_id", "never_expires", "number_of_billing_cycles", "quantity"]},
+                    {"remove": ["_any_key_"]}
+                ]
+            }
+        ]
+
diff --git a/braintree/plan_gateway.py b/braintree/plan_gateway.py
index c6f1198..2cbf432 100644
--- a/braintree/plan_gateway.py
+++ b/braintree/plan_gateway.py
@@ -15,3 +15,33 @@ class PlanGateway(object):
     def all(self):
         response = self.config.http().get(self.config.base_merchant_path() + "/plans/")
         return [Plan(self.gateway, item) for item in ResourceCollection._extract_as_array(response, "plans")]
+
+    def create(self, params=None):
+        if params is None:
+            params = {}
+        Resource.verify_keys(params, Plan.create_signature())
+        response = self.config.http().post(self.config.base_merchant_path() + "/plans", {"plan": params})
+        if "plan" in response:
+            return SuccessfulResult({"plan": Plan(self.gateway, response["plan"])})
+        elif "api_error_response" in response:
+            return ErrorResult(self.gateway, response["api_error_response"])
+
+    def find(self, plan_id):
+        try:
+            if plan_id is None or plan_id.strip() == "":
+                raise NotFoundError()
+            response = self.config.http().get(self.config.base_merchant_path() + "/plans/" + plan_id)
+            return Plan(self.gateway, response["plan"])
+        except NotFoundError:
+            raise NotFoundError("Plan with id " + repr(plan_id) + " not found")
+
+    def update(self, plan_id, params=None):
+        if params is None:
+            params = {}
+        Resource.verify_keys(params, Plan.update_signature())
+        response = self.config.http().put(self.config.base_merchant_path() + "/plans/" + plan_id, {"plan": params})
+        if "plan" in response:
+            return SuccessfulResult({"plan": Plan(self.gateway, response["plan"])})
+        elif "api_error_response" in response:
+            return ErrorResult(self.gateway, response["api_error_response"])
+
diff --git a/braintree/resource.py b/braintree/resource.py
index 41d176f..23c90fb 100644
--- a/braintree/resource.py
+++ b/braintree/resource.py
@@ -3,8 +3,8 @@ import string
 import sys
 from braintree.attribute_getter import AttributeGetter
 
-text_type = unicode if sys.version_info[0] == 2 else str
-raw_type = str if sys.version_info[0] == 2 else bytes
+text_type = str
+raw_type = bytes
 
 class Resource(AttributeGetter):
     @staticmethod
@@ -51,10 +51,14 @@ class Resource(AttributeGetter):
 
     @staticmethod
     def __remove_wildcard_keys(allowed_keys, invalid_keys):
-        wildcard_keys = [re.sub("(?<=[^\\\\])_", "\\_", re.escape(key)).replace("\\[\\_\\_any\\_key\\_\\_\\]", "\\[[\w-]+\\]") for key in allowed_keys if re.search("\\[__any_key__\\]", key)]
+        wildcard_keys = [
+            re.sub(r"(?<=[^\\])_", "\\_", re.escape(key)).replace(r"\[\_\_any\_key\_\_\]", r"\[[\w-]+\]")
+            for key in allowed_keys
+            if re.search(r"\[__any_key__\]", key)
+        ]
         new_keys = []
         for key in invalid_keys:
-            if len([match for match in wildcard_keys if re.match("\A" + match + "\Z", key)]) == 0:
+            if len([match for match in wildcard_keys if re.match(r"\A" + match + r"\Z", key)]) == 0:
                 new_keys.append(key)
         return new_keys
 
diff --git a/braintree/risk_data.py b/braintree/risk_data.py
index 0882740..fa2b8b6 100644
--- a/braintree/risk_data.py
+++ b/braintree/risk_data.py
@@ -1,4 +1,8 @@
 from braintree.attribute_getter import AttributeGetter
+from braintree.liability_shift import LiabilityShift
 
 class RiskData(AttributeGetter):
-    pass
+    def __init__(self, attributes):
+        AttributeGetter.__init__(self, attributes)
+        if "liability_shift" in attributes:
+            self.liability_shift = LiabilityShift(attributes["liability_shift"])
diff --git a/braintree/search.py b/braintree/search.py
index a8e290a..fc8a4cb 100644
--- a/braintree/search.py
+++ b/braintree/search.py
@@ -1,3 +1,5 @@
+import warnings
+
 class Search:
     """
     Collection of classes used to build search queries.
@@ -73,6 +75,8 @@ class Search:
     class MultipleValueNodeBuilder(object):
         """Builds a query to check membership in a sequence."""
         def __init__(self, name, whitelist = []):
+            if "chargeback_protection_level" == name:
+                warnings.warn("Use protection_level parameter instead", DeprecationWarning)
             self.name = name
             self.whitelist = whitelist
 
diff --git a/braintree/sepa_direct_debit_account.py b/braintree/sepa_direct_debit_account.py
new file mode 100644
index 0000000..d4f9548
--- /dev/null
+++ b/braintree/sepa_direct_debit_account.py
@@ -0,0 +1,18 @@
+import braintree
+from braintree.resource import Resource
+from braintree.configuration import Configuration
+
+
+class SepaDirectDebitAccount(Resource):
+    @staticmethod
+    def find(sepa_direct_debit_account_token):
+        return Configuration.gateway().sepa_direct_debit_account.find(sepa_direct_debit_account_token)
+
+    @staticmethod
+    def delete(sepa_direct_debit_account_token):
+        return Configuration.gateway().sepa_direct_debit_account.delete(sepa_direct_debit_account_token)
+
+    def __init__(self, gateway, attributes):
+        Resource.__init__(self, gateway, attributes)
+        if "subscriptions" in attributes:
+            self.subscriptions = [braintree.subscription.Subscription(gateway, subscription) for subscription in self.subscriptions]
diff --git a/braintree/sepa_direct_debit_account_gateway.py b/braintree/sepa_direct_debit_account_gateway.py
new file mode 100644
index 0000000..3c3d2d9
--- /dev/null
+++ b/braintree/sepa_direct_debit_account_gateway.py
@@ -0,0 +1,27 @@
+import braintree
+from braintree.sepa_direct_debit_account import SepaDirectDebitAccount
+from braintree.error_result import ErrorResult
+from braintree.exceptions.not_found_error import NotFoundError
+from braintree.resource import Resource
+from braintree.successful_result import SuccessfulResult
+
+
+class SepaDirectDebitAccountGateway(object):
+    def __init__(self, gateway):
+        self.gateway = gateway
+        self.config = gateway.config
+
+    def find(self, sepa_direct_debit_account_token):
+        try:
+            if sepa_direct_debit_account_token is None or sepa_direct_debit_account_token.strip() == "":
+                raise NotFoundError()
+
+            response = self.config.http().get(self.config.base_merchant_path() + "/payment_methods/sepa_debit_account/" + sepa_direct_debit_account_token)
+            if "sepa_debit_account" in response:
+                return SepaDirectDebitAccount(self.gateway, response["sepa_debit_account"])
+        except NotFoundError:
+            raise NotFoundError("sepa direct debit account with token " + repr(sepa_direct_debit_account_token) + " not found")
+
+    def delete(self, sepa_direct_debit_account_token):
+        self.config.http().delete(self.config.base_merchant_path() + "/payment_methods/sepa_debit_account/" + sepa_direct_debit_account_token)
+        return SuccessfulResult()
diff --git a/braintree/subscription.py b/braintree/subscription.py
index a9b7d4d..4ba61d7 100644
--- a/braintree/subscription.py
+++ b/braintree/subscription.py
@@ -14,6 +14,7 @@ from braintree.transaction import Transaction
 from braintree.resource import Resource
 from braintree.configuration import Configuration
 
+
 class Subscription(Resource):
     """
     A class representing a Subscription.
@@ -31,10 +32,11 @@ class Subscription(Resource):
             "trial_period": True
         })
 
-    For more information on Subscriptions, see https://developers.braintreepayments.com/reference/request/subscription/create/python
+    For more information on Subscriptions, see https://developer.paypal.com/braintree/docs/reference/request/subscription/create/python
 
     """
 
+    # NEXT_MAJOR_VERSION this can be an enum! they were added as of python 3.4 and we support 3.5+
     class TrialDurationUnit(object):
         """
         Constants representing trial duration units.  Available types are:
@@ -46,15 +48,16 @@ class Subscription(Resource):
         Day = "day"
         Month = "month"
 
+    # NEXT_MAJOR_VERSION this can be an enum! they were added as of python 3.4 and we support 3.5+
     class Source(object):
         Api          = "api"
         ControlPanel = "control_panel"
         Recurring    = "recurring"
-        Unrecognized = "unrecognized"
 
+    # NEXT_MAJOR_VERSION this can be an enum! they were added as of python 3.4 and we support 3.5+
     class Status(object):
         """
-        Constants representing subscription statusues.  Available statuses are:
+        Constants representing subscription statuses.  Available statuses are:
 
         * braintree.Subscription.Status.Active
         * braintree.Subscription.Status.Canceled
@@ -70,7 +73,7 @@ class Subscription(Resource):
         Pending = "Pending"
 
     @staticmethod
-    def create(params={}):
+    def create(params=None):
         """
         Create a Subscription
 
@@ -82,7 +85,8 @@ class Subscription(Resource):
             })
 
         """
-
+        if params is None:
+            params = {}
         return Configuration.gateway().subscription.create(params)
 
     @staticmethod
@@ -102,14 +106,14 @@ class Subscription(Resource):
             "trial_duration_unit",
             "trial_period",
             {
-                "descriptor": [ "name", "phone", "url" ]
+                "descriptor": ["name", "phone", "url"]
             },
             {
                 "options": [
                     "do_not_inherit_add_ons_or_discounts",
                     "start_immediately",
-                    { 
-                        "paypal": [ "description" ] 
+                    {
+                        "paypal": ["description"]
                     }
                 ]
             }
@@ -127,17 +131,12 @@ class Subscription(Resource):
 
         return Configuration.gateway().subscription.find(subscription_id)
 
-    @staticmethod
-    def retryCharge(subscription_id, amount=None):
-        warnings.warn("Please use Subscription.retry_charge instead", DeprecationWarning)
-        return Subscription.retry_charge(subscription_id, amount)
-
     @staticmethod
     def retry_charge(subscription_id, amount=None, submit_for_settlement=False):
         return Configuration.gateway().subscription.retry_charge(subscription_id, amount, submit_for_settlement)
 
     @staticmethod
-    def update(subscription_id, params={}):
+    def update(subscription_id, params=None):
         """
         Update an existing subscription
 
@@ -149,7 +148,8 @@ class Subscription(Resource):
             })
 
         """
-
+        if params is None:
+            params = {}
         return Configuration.gateway().subscription.update(subscription_id, params)
 
     @staticmethod
@@ -177,7 +177,7 @@ class Subscription(Resource):
         - status
 
         For text fields, you can search using the following operators: ==, !=, starts_with, ends_with
-        and contains. For mutiple value fields, you can search using the in_list operator. An example::
+        and contains. For multiple value fields, you can search using the in_list operator. An example::
 
             braintree.Subscription.search([
                 braintree.SubscriptionSearch.plan_id.starts_with("abc"),
@@ -203,9 +203,9 @@ class Subscription(Resource):
                 "descriptor": [ "name", "phone", "url" ]
             },
             {
-                "options": [ 
-                    "prorate_charges", 
-                    "replace_all_add_ons_and_discounts", 
+                "options": [
+                    "prorate_charges",
+                    "replace_all_add_ons_and_discounts",
                     "revert_subscription_on_proration_failure",
                     {
                         "paypal": [ "description" ]
@@ -232,9 +232,6 @@ class Subscription(Resource):
         ]
 
     def __init__(self, gateway, attributes):
-        if "next_bill_amount" in attributes:
-            self._next_bill_amount = Decimal(attributes["next_bill_amount"])
-            del(attributes["next_bill_amount"])
         Resource.__init__(self, gateway, attributes)
         if "price" in attributes:
             self.price = Decimal(self.price)
@@ -247,15 +244,10 @@ class Subscription(Resource):
         if "descriptor" in attributes:
             self.descriptor = Descriptor(gateway, attributes.pop("descriptor"))
         if "description" in attributes:
-            self.description = attributes["description"] 
+            self.description = attributes["description"]
         if "discounts" in attributes:
             self.discounts = [Discount(gateway, discount) for discount in self.discounts]
         if "status_history" in attributes:
             self.status_history = [SubscriptionStatusEvent(gateway, status_event) for status_event in self.status_history]
         if "transactions" in attributes:
             self.transactions = [Transaction(gateway, transaction) for transaction in self.transactions]
-
-    @property
-    def next_bill_amount(self):
-        warnings.warn("Please use Subscription.next_billing_period_amount instead", DeprecationWarning)
-        return self._next_bill_amount
diff --git a/braintree/subscription_gateway.py b/braintree/subscription_gateway.py
index e40c1f5..34a33a3 100644
--- a/braintree/subscription_gateway.py
+++ b/braintree/subscription_gateway.py
@@ -8,6 +8,7 @@ from braintree.resource_collection import ResourceCollection
 from braintree.successful_result import SuccessfulResult
 from braintree.transaction import Transaction
 
+
 class SubscriptionGateway(object):
     def __init__(self, gateway):
         self.gateway = gateway
@@ -20,7 +21,9 @@ class SubscriptionGateway(object):
         elif "api_error_response" in response:
             return ErrorResult(self.gateway, response["api_error_response"])
 
-    def create(self, params={}):
+    def create(self, params=None):
+        if params is None:
+            params = {}
         Resource.verify_keys(params, Subscription.create_signature())
         response = self.config.http().post(self.config.base_merchant_path() + "/subscriptions", {"subscription": params})
         if "subscription" in response:
@@ -56,7 +59,9 @@ class SubscriptionGateway(object):
         response = self.config.http().post(self.config.base_merchant_path() + "/subscriptions/advanced_search_ids", {"search": self.__criteria(query)})
         return ResourceCollection(query, response, self.__fetch)
 
-    def update(self, subscription_id, params={}):
+    def update(self, subscription_id, params=None):
+        if params is None:
+            params = {}
         Resource.verify_keys(params, Subscription.update_signature())
         response = self.config.http().put(self.config.base_merchant_path() + "/subscriptions/" + subscription_id, {"subscription": params})
         if "subscription" in response:
diff --git a/braintree/test/authentication_ids.py b/braintree/test/authentication_ids.py
new file mode 100644
index 0000000..593845e
--- /dev/null
+++ b/braintree/test/authentication_ids.py
@@ -0,0 +1,16 @@
+class AuthenticationIds(object):
+    ThreeDSecureVisaFullAuthentication = "fake-three-d-secure-visa-full-authentication-id"
+    ThreeDSecureVisaLookupTimeout = "fake-three-d-secure-visa-lookup-timeout-id"
+    ThreeDSecureVisaFailedSignature = "fake-three-d-secure-visa-failed-signature-id"
+    ThreeDSecureVisaFailedAuthentication = "fake-three-d-secure-visa-failed-authentication-id"
+    ThreeDSecureVisaAttemptsNonParticipating = "fake-three-d-secure-visa-attempts-non-participating-id"
+    ThreeDSecureVisaNoteEnrolled = "fake-three-d-secure-visa-not-enrolled-id"
+    ThreeDSecureVisaUnavailable = "fake-three-d-secure-visa-unavailable-id"
+    ThreeDSecureVisaMPILookupError = "fake-three-d-secure-visa-mpi-lookup-error-id"
+    ThreeDSecureVisaMPIAuthenticateError = "fake-three-d-secure-visa-mpi-authenticate-error-id"
+    ThreeDSecureVisaAuthenticationUnavailable = "fake-three-d-secure-visa-authentication-unavailable-id"
+    ThreeDSecureVisaBypassedAuthentication = "fake-three-d-secure-visa-bypassed-authentication-id"
+    ThreeDSecureTwoVisaSuccessfulFrictionlessAuthentication = "fake-three-d-secure-two-visa-successful-frictionless-authentication-id"
+    ThreeDSecureTwoVisaSuccessfulStepUpAuthentication = "fake-three-d-secure-two-visa-successful-step-up-authentication-id"
+    ThreeDSecureTwoVisaErrorOnLookup = "fake-three-d-secure-two-visa-error-on-lookup-id"
+    ThreeDSecureTwoVisaTimeoutOnLookup = "fake-three-d-secure-two-visa-timeout-on-lookup-id"
diff --git a/braintree/test/credit_card_numbers.py b/braintree/test/credit_card_numbers.py
index 1e73a88..07f852d 100644
--- a/braintree/test/credit_card_numbers.py
+++ b/braintree/test/credit_card_numbers.py
@@ -25,6 +25,7 @@ class CreditCardNumbers(object):
 
     Hiper = "6370950000000005"
     Hipercard = "6062820524845321"
+    Amex = "378734493671000"
 
     class FailsSandboxVerification(object):
         AmEx       = "378734493671000"
diff --git a/braintree/test/nonces.py b/braintree/test/nonces.py
index 5990ed5..e38916e 100644
--- a/braintree/test/nonces.py
+++ b/braintree/test/nonces.py
@@ -2,6 +2,7 @@ class Nonces(object):
     Transactable = "fake-valid-nonce"
     Consumed = "fake-consumed-nonce"
     PayPalOneTimePayment = "fake-paypal-one-time-nonce"
+    # NEXT_MAJOR_VERSION - no longer supported in the Gateway, remove this constant
     PayPalFuturePayment = "fake-paypal-future-nonce"
     PayPalBillingAgreement = "fake-paypal-billing-agreement-nonce"
     ApplePayVisa = "fake-apple-pay-visa-nonce"
@@ -9,16 +10,31 @@ class Nonces(object):
     ApplePayAmEx = "fake-apple-pay-amex-nonce"
     AbstractTransactable = "fake-abstract-transactable-nonce"
     Europe = "fake-europe-bank-account-nonce"
-    Coinbase = "fake-coinbase-nonce"
+    # NEXT_MAJOR_VERSION - rename AndroidPay to GooglePay
     AndroidPayCard = "fake-android-pay-nonce"
     AndroidPayCardDiscover = "fake-android-pay-discover-nonce"
     AndroidPayCardVisa = "fake-android-pay-visa-nonce"
     AndroidPayCardMasterCard = "fake-android-pay-mastercard-nonce"
     AndroidPayCardAmEx = "fake-android-pay-amex-nonce"
+    # NEXT_MAJOR_VERSION remove amex express checkout
     AmexExpressCheckoutCard = "fake-amex-express-checkout-nonce"
     VenmoAccount = "fake-venmo-account-nonce"
     VenmoAccountTokenIssuanceError = "fake-token-issuance-error-venmo-account-nonce"
     ThreeDSecureVisaFullAuthentication = "fake-three-d-secure-visa-full-authentication-nonce"
+    ThreeDSecureVisaLookupTimeout = "fake-three-d-secure-visa-lookup-timeout-nonce"
+    ThreeDSecureVisaFailedSignature = "fake-three-d-secure-visa-failed-signature-nonce"
+    ThreeDSecureVisaFailedAuthentication = "fake-three-d-secure-visa-failed-authentication-nonce"
+    ThreeDSecureVisaAttemptsNonParticipating = "fake-three-d-secure-visa-attempts-non-participating-nonce"
+    ThreeDSecureVisaNoteEnrolled = "fake-three-d-secure-visa-not-enrolled-nonce"
+    ThreeDSecureVisaUnavailable = "fake-three-d-secure-visa-unavailable-nonce"
+    ThreeDSecureVisaMPILookupError = "fake-three-d-secure-visa-mpi-lookup-error-nonce"
+    ThreeDSecureVisaMPIAuthenticateError = "fake-three-d-secure-visa-mpi-authenticate-error-nonce"
+    ThreeDSecureVisaAuthenticationUnavailable = "fake-three-d-secure-visa-authentication-unavailable-nonce"
+    ThreeDSecureVisaBypassedAuthentication = "fake-three-d-secure-visa-bypassed-authentication-nonce"
+    ThreeDSecureTwoVisaSuccessfulFrictionlessAuthentication = "fake-three-d-secure-two-visa-successful-frictionless-authentication-nonce"
+    ThreeDSecureTwoVisaSuccessfulStepUpAuthentication = "fake-three-d-secure-two-visa-successful-step-up-authentication-nonce"
+    ThreeDSecureTwoVisaErrorOnLookup = "fake-three-d-secure-two-visa-error-on-lookup-nonce"
+    ThreeDSecureTwoVisaTimeoutOnLookup = "fake-three-d-secure-two-visa-timeout-on-lookup-nonce"
     TransactableVisa = "fake-valid-visa-nonce"
     TransactableAmEx = "fake-valid-amex-nonce"
     TransactableMasterCard = "fake-valid-mastercard-nonce"
@@ -47,6 +63,8 @@ class Nonces(object):
     PayPalFuturePaymentRefreshToken = "fake-paypal-future-refresh-token-nonce"
     SEPA = "fake-sepa-bank-account-nonce"
     GatewayRejectedFraud = "fake-gateway-rejected-fraud-nonce"
+    GatewayRejectedRiskThreshold = "fake-gateway-rejected-risk-thresholds-nonce"
+    # NEXT_MAJOR_VERSION remove masterpass
     MasterpassAmEx = "fake-masterpass-amex-nonce"
     MasterpassDiscover = "fake-masterpass-discover-nonce"
     MasterpassMasterCard = "fake-masterpass-mastercard-nonce"
@@ -59,3 +77,4 @@ class Nonces(object):
     SamsungPayDiscover = "tokensam_fake_american_express"
     SamsungPayMasterCard = "tokensam_fake_mastercard"
     SamsungPayVisa = "tokensam_fake_visa"
+    SepaDirectDebit = "fake-sepa-direct-debit-nonce"
diff --git a/braintree/testing_gateway.py b/braintree/testing_gateway.py
index 7bc8722..9aacf54 100644
--- a/braintree/testing_gateway.py
+++ b/braintree/testing_gateway.py
@@ -38,7 +38,7 @@ class TestingGateway(object):
         response = self.config.http().post(self.config.base_merchant_path() + "/three_d_secure/create_verification/" + merchant_account_id, {
             "three_d_secure_verification": params
         })
-        return response["three_d_secure_verification"]["three_d_secure_token"]
+        return response["three_d_secure_verification"]["three_d_secure_authentication_id"]
 
     def __create_result(self, response):
         if "transaction" in response:
diff --git a/braintree/transaction.py b/braintree/transaction.py
index 4e1a2f9..eab179a 100644
--- a/braintree/transaction.py
+++ b/braintree/transaction.py
@@ -2,45 +2,43 @@ import braintree
 import warnings
 from decimal import Decimal
 from braintree.add_on import AddOn
+from braintree.address import Address
+from braintree.amex_express_checkout_card import AmexExpressCheckoutCard
+from braintree.android_pay_card import AndroidPayCard
 from braintree.apple_pay_card import ApplePayCard
 from braintree.authorization_adjustment import AuthorizationAdjustment
-from braintree.coinbase_account import CoinbaseAccount
-from braintree.android_pay_card import AndroidPayCard
-from braintree.amex_express_checkout_card import AmexExpressCheckoutCard
-from braintree.venmo_account import VenmoAccount
-from braintree.disbursement_detail import DisbursementDetail
-from braintree.dispute import Dispute
-from braintree.discount import Discount
-from braintree.successful_result import SuccessfulResult
-from braintree.status_event import StatusEvent
-from braintree.error_result import ErrorResult
-from braintree.resource import Resource
-from braintree.address import Address
 from braintree.configuration import Configuration
 from braintree.credit_card import CreditCard
 from braintree.customer import Customer
+from braintree.descriptor import Descriptor
+from braintree.disbursement_detail import DisbursementDetail
+from braintree.discount import Discount
+from braintree.dispute import Dispute
+from braintree.error_result import ErrorResult
+from braintree.europe_bank_account import EuropeBankAccount
+from braintree.exceptions.not_found_error import NotFoundError
+from braintree.facilitated_details import FacilitatedDetails
+from braintree.facilitator_details import FacilitatorDetails
+from braintree.liability_shift import LiabilityShift
+from braintree.local_payment import LocalPayment
+from braintree.masterpass_card import MasterpassCard
+from braintree.payment_instrument_type import PaymentInstrumentType
 from braintree.paypal_account import PayPalAccount
 from braintree.paypal_here import PayPalHere
-from braintree.europe_bank_account import EuropeBankAccount
-from braintree.subscription_details import SubscriptionDetails
+from braintree.resource import Resource
 from braintree.resource_collection import ResourceCollection
-from braintree.transparent_redirect import TransparentRedirect
-from braintree.exceptions.not_found_error import NotFoundError
-from braintree.descriptor import Descriptor
 from braintree.risk_data import RiskData
+from braintree.samsung_pay_card import SamsungPayCard
+from braintree.sepa_direct_debit_account import SepaDirectDebitAccount
+from braintree.status_event import StatusEvent
+from braintree.subscription_details import SubscriptionDetails
+from braintree.successful_result import SuccessfulResult
 from braintree.three_d_secure_info import ThreeDSecureInfo
 from braintree.transaction_line_item import TransactionLineItem
 from braintree.us_bank_account import UsBankAccount
-# NEXT_MAJOR_VERSION Remove this class as legacy Ideal has been removed/disabled in the Braintree Gateway
-# DEPRECATED If you're looking to accept iDEAL as a payment method contact accounts@braintreepayments.com for a solution.
-from braintree.ideal_payment import IdealPayment
-from braintree.local_payment import LocalPayment
+from braintree.venmo_account import VenmoAccount
 from braintree.visa_checkout_card import VisaCheckoutCard
-from braintree.masterpass_card import MasterpassCard
-from braintree.facilitated_details import FacilitatedDetails
-from braintree.facilitator_details import FacilitatorDetails
-from braintree.payment_instrument_type import PaymentInstrumentType
-from braintree.samsung_pay_card import SamsungPayCard
+
 
 class Transaction(Resource):
     """
@@ -93,15 +91,17 @@ class Transaction(Resource):
         print(result.transaction.amount)
         print(result.transaction.order_id)
 
-    For more information on Transactions, see https://developers.braintreepayments.com/reference/request/transaction/sale/python
+    For more information on Transactions, see https://developer.paypal.com/braintree/docs/reference/request/transaction/sale/python
 
     """
 
     def __repr__(self):
       detail_list = [
         "id",
+        "graphql_id",
         "additional_processor_response",
         "amount",
+        "acquirer_reference_number",
         "authorization_adjustments",
         "authorization_expires_at",
         "avs_error_response_code",
@@ -117,6 +117,8 @@ class Transaction(Resource):
         "disputes",
         "escrow_status",
         "gateway_rejection_reason",
+        "installments",
+        "liability_shift",
         "master_merchant_account_id",
         "merchant_account_id",
         "network_response_code",
@@ -126,6 +128,7 @@ class Transaction(Resource):
         "payment_instrument_type",
         "payment_method_token",
         "plan_id",
+        "processed_with_network_token",
         "processor_authorization_code",
         "processor_response_code",
         "processor_response_text",
@@ -135,6 +138,8 @@ class Transaction(Resource):
         "recurring",
         "refund_id",
         "refunded_transaction_id",
+        "retried",
+        "retrieval_reference_number",
         "service_fee_amount",
         "settlement_batch_id",
         "shipping_amount",
@@ -152,6 +157,7 @@ class Transaction(Resource):
 
       return super(Transaction, self).__repr__(detail_list)
 
+    # NEXT_MAJOR_VERSION this can be an enum! they were added as of python 3.4 and we support 3.5+
     class CreatedUsing(object):
         """
         Constants representing how the transaction was created.  Available types are:
@@ -162,8 +168,8 @@ class Transaction(Resource):
 
         FullInformation = "full_information"
         Token           = "token"
-        Unrecognized    = "unrecognized"
 
+    # NEXT_MAJOR_VERSION this can be an enum! they were added as of python 3.4 and we support 3.5+
     class GatewayRejectionReason(object):
         """
         Constants representing gateway rejection reasons. Available types are:
@@ -172,25 +178,34 @@ class Transaction(Resource):
         * braintree.Transaction.GatewayRejectionReason.AvsAndCvv
         * braintree.Transaction.GatewayRejectionReason.Cvv
         * braintree.Transaction.GatewayRejectionReason.Duplicate
+        * braintree.Transaction.GatewayRejectionReason.ExcessiveRetry
         * braintree.Transaction.GatewayRejectionReason.Fraud
+        * braintree.Transaction.GatewayRejectionReason.RiskThreshold
         * braintree.Transaction.GatewayRejectionReason.ThreeDSecure
+        * braintree.Transaction.GatewayRejectionReason.TokenIssuance
         """
         ApplicationIncomplete = "application_incomplete"
         Avs                   = "avs"
         AvsAndCvv             = "avs_and_cvv"
         Cvv                   = "cvv"
         Duplicate             = "duplicate"
+        ExcessiveRetry        = "excessive_retry"
         Fraud                 = "fraud"
+        RiskThreshold         = "risk_threshold"
         ThreeDSecure          = "three_d_secure"
         TokenIssuance         = "token_issuance"
-        Unrecognized          = "unrecognized"
 
+    # NEXT_MAJOR_VERSION this can be an enum! they were added as of python 3.4 and we support 3.5+
+    class ReasonCode(object):
+        ANY_REASON_CODE = 'any_reason_code'
+
+    # NEXT_MAJOR_VERSION this can be an enum! they were added as of python 3.4 and we support 3.5+
     class Source(object):
         Api          = "api"
         ControlPanel = "control_panel"
         Recurring    = "recurring"
-        Unrecognized = "unrecognized"
 
+    # NEXT_MAJOR_VERSION this can be an enum! they were added as of python 3.4 and we support 3.5+
     class EscrowStatus(object):
         """
         Constants representing transaction escrow statuses. Available statuses are:
@@ -207,8 +222,8 @@ class Transaction(Resource):
         ReleasePending = "release_pending"
         Released       = "released"
         Refunded       = "refunded"
-        Unrecognized   = "unrecognized"
 
+    # NEXT_MAJOR_VERSION this can be an enum! they were added as of python 3.4 and we support 3.5+
     class Status(object):
         """
         Constants representing transaction statuses. Available statuses are:
@@ -241,9 +256,8 @@ class Transaction(Resource):
         Settling               = "settling"
         SubmittedForSettlement = "submitted_for_settlement"
         Voided                 = "voided"
-        # NEXT_MAJOR_VERSION this is never used and should be removed
-        Unrecognized           = "unrecognized"
 
+    # NEXT_MAJOR_VERSION this can be an enum! they were added as of python 3.4 and we support 3.5+
     class Type(object):
         """
         Constants representing transaction types. Available types are:
@@ -255,11 +269,13 @@ class Transaction(Resource):
         Credit = "credit"
         Sale = "sale"
 
+    # NEXT_MAJOR_VERSION this can be an enum! they were added as of python 3.4 and we support 3.5+
     class IndustryType(object):
         Lodging = "lodging"
         TravelAndCruise = "travel_cruise"
         TravelAndFlight = "travel_flight"
 
+    # NEXT_MAJOR_VERSION this can be an enum! they were added as of python 3.4 and we support 3.5+
     class AdditionalCharge(object):
         Restaurant = "restaurant"
         GiftShop = "gift_shop"
@@ -268,6 +284,18 @@ class Transaction(Resource):
         Laundry = "laundry"
         Other = "other"
 
+    @staticmethod
+    def adjust_authorization(transaction_id, amount):
+        """
+        adjust authorization for an existing transaction.
+
+        It expects a `transaction_id` and `amount`, which is the new total authorization amount
+
+        result = braintree.Transaction.adjust_authorization("my_transaction_id", "amount")
+
+        """
+        return Configuration.gateway().transaction.adjust_authorization(transaction_id, amount)
+
     @staticmethod
     def clone_transaction(transaction_id, params):
         return Configuration.gateway().transaction.clone_transaction(transaction_id, params)
@@ -286,19 +314,7 @@ class Transaction(Resource):
         return Configuration.gateway().transaction.cancel_release(transaction_id)
 
     @staticmethod
-    def confirm_transparent_redirect(query_string):
-        """
-        Confirms a transparent redirect request. It expects the query string from the
-        redirect request. The query string should _not_ include the leading "?" character. ::
-
-            result = braintree.Transaction.confirm_transparent_redirect_request("foo=bar&id=12345")
-        """
-
-        warnings.warn("Please use TransparentRedirect.confirm instead", DeprecationWarning)
-        return Configuration.gateway().transaction.confirm_transparent_redirect(query_string)
-
-    @staticmethod
-    def credit(params={}):
+    def credit(params=None):
         """
         Creates a transaction of type Credit.
 
@@ -324,7 +340,8 @@ class Transaction(Resource):
             })
 
         """
-
+        if params is None:
+            params = {}
         params["type"] = Transaction.Type.Credit
         return Transaction.create(params)
 
@@ -339,14 +356,6 @@ class Transaction(Resource):
         """
         return Configuration.gateway().transaction.find(transaction_id)
 
-    @staticmethod
-    def line_items(transaction_id):
-        """
-        Find a transaction's line items, given a transaction_id. This does not return
-        a result object. This will raise a :class:`NotFoundError <braintree.exceptions.not_found_error.NotFoundError>` if the provided transaction_id is not found. ::
-        """
-        return Configuration.gateway().transaction_line_item.find_all(transaction_id)
-
     @staticmethod
     def hold_in_escrow(transaction_id):
         """
@@ -374,7 +383,7 @@ class Transaction(Resource):
 
 
     @staticmethod
-    def sale(params={}):
+    def sale(params=None):
         """
         Creates a transaction of type Sale. Amount is required. Also, a credit card,
         customer_id or payment_method_token is required. ::
@@ -397,7 +406,10 @@ class Transaction(Resource):
                 "customer_id": "my_customer_id"
             })
         """
-
+        if params is None:
+            params = {}
+        if "recurring" in params.keys():
+            warnings.warn("Use transaction_source parameter instead", DeprecationWarning)
         params["type"] = Transaction.Type.Sale
         return Transaction.create(params)
 
@@ -419,7 +431,7 @@ class Transaction(Resource):
         return Configuration.gateway().transaction.release_from_escrow(transaction_id)
 
     @staticmethod
-    def submit_for_settlement(transaction_id, amount=None, params={}):
+    def submit_for_settlement(transaction_id, amount=None, params=None):
         """
         Submits an authorized transaction for settlement.
 
@@ -428,13 +440,14 @@ class Transaction(Resource):
             result = braintree.Transaction.submit_for_settlement("my_transaction_id")
 
         """
-
+        if params is None:
+            params = {}
         return Configuration.gateway().transaction.submit_for_settlement(transaction_id, amount, params)
 
     @staticmethod
-    def update_details(transaction_id, params={}):
+    def update_details(transaction_id, params=None):
         """
-        Updates exisiting details for transaction submtted_for_settlement.
+        Updates existing details for transaction submitted_for_settlement.
 
         Requires the transaction id::
 
@@ -449,32 +462,10 @@ class Transaction(Resource):
             )
 
         """
-
+        if params is None:
+            params = {}
         return Configuration.gateway().transaction.update_details(transaction_id, params)
 
-    @staticmethod
-    def tr_data_for_credit(tr_data, redirect_url):
-        """
-        Builds tr_data for a Transaction of type Credit
-        """
-        return Configuration.gateway().transaction.tr_data_for_credit(tr_data, redirect_url)
-
-    @staticmethod
-    def tr_data_for_sale(tr_data, redirect_url):
-        """
-        Builds tr_data for a Transaction of type Sale
-        """
-        return Configuration.gateway().transaction.tr_data_for_sale(tr_data, redirect_url)
-
-    @staticmethod
-    def transparent_redirect_create_url():
-        """
-        Returns the url to be used for creating Transactions through transparent redirect.
-        """
-
-        warnings.warn("Please use TransparentRedirect.url instead", DeprecationWarning)
-        return Configuration.gateway().transaction.transparent_redirect_create_url()
-
     @staticmethod
     def void(transaction_id):
         """
@@ -524,20 +515,23 @@ class Transaction(Resource):
     @staticmethod
     def create_signature():
         return [
-            "amount", "customer_id", "device_session_id", "fraud_merchant_id", "merchant_account_id", "order_id", "channel",
+            "amount", "customer_id", "merchant_account_id", "order_id", "channel",
             "payment_method_token", "purchase_order_number", "recurring", "transaction_source", "shipping_address_id",
-            "device_data", "billing_address_id", "payment_method_nonce", "tax_amount",
+            "device_data", "billing_address_id", "payment_method_nonce", "product_sku", "tax_amount",
             "shared_payment_method_token", "shared_customer_id", "shared_billing_address_id", "shared_shipping_address_id", "shared_payment_method_nonce",
             "discount_amount", "shipping_amount", "ships_from_postal_code",
-            "tax_exempt", "three_d_secure_token", "type", "venmo_sdk_payment_method_code", "service_fee_amount",
+            "tax_exempt", "three_d_secure_authentication_id", "three_d_secure_token", "type", "venmo_sdk_payment_method_code", "service_fee_amount",
+            "sca_exemption","exchange_rate_quote_id",
+            "device_session_id", "fraud_merchant_id", # NEXT_MAJOR_VERSION remove device_session_id and fraud_merchant_id
             {
                 "risk_data": [
-                    "customer_browser", "customer_ip"
+                    "customer_browser", "customer_device_id", "customer_ip", "customer_location_zip", "customer_tenure"
                 ]
             },
             {
                 "credit_card": [
-                    "token", "cardholder_name", "cvv", "expiration_date", "expiration_month", "expiration_year", "number"
+                    "token", "cardholder_name", "cvv", "expiration_date", "expiration_month", "expiration_year", "number",
+                    {"payment_reader_card_details": ["encrypted_card_data", "key_serial_number"]}
                 ]
             },
             {
@@ -549,14 +543,14 @@ class Transaction(Resource):
                 "billing": [
                     "first_name", "last_name", "company", "country_code_alpha2", "country_code_alpha3",
                     "country_code_numeric", "country_name", "extended_address", "locality",
-                    "postal_code", "region", "street_address"
+                    "phone_number", "postal_code", "region", "street_address"
                 ]
             },
             {
                 "shipping": [
                     "first_name", "last_name", "company", "country_code_alpha2", "country_code_alpha3",
                     "country_code_numeric", "country_name", "extended_address", "locality",
-                    "postal_code", "region", "street_address"
+                    "phone_number", "postal_code", "region", "shipping_method", "street_address"
                 ]
             },
             {
@@ -655,11 +649,36 @@ class Transaction(Resource):
                     "quantity", "name", "description", "kind", "unit_amount", "unit_tax_amount", "total_amount", "discount_amount", "tax_amount", "unit_of_measure", "product_code", "commodity_code", "url",
                 ]
             },
+            {"apple_pay_card": ["number", "cardholder_name", "cryptogram", "expiration_month", "expiration_year", "eci_indicator"]},
+            # NEXT_MAJOR_VERSION use google_pay_card in public API (map to android_pay_card internally)
+            {"android_pay_card": ["number", "cryptogram", "expiration_month", "expiration_year", "eci_indicator", "source_card_type", "source_card_last_four", "google_transaction_id"]},
+            {"installments": {"count"}},
         ]
 
     @staticmethod
     def submit_for_settlement_signature():
-        return ["order_id", {"descriptor": ["name", "phone", "url"]}]
+        return [
+                "order_id",
+                {"descriptor": ["name", "phone", "url"]},
+                "purchase_order_number",
+                "tax_amount",
+                "tax_exempt",
+                "discount_amount",
+                "shipping_amount",
+                "ships_from_postal_code",
+                {"line_items":
+                    [
+                        "quantity", "name", "description", "kind", "unit_amount", "unit_tax_amount", "total_amount", "discount_amount", "tax_amount", "unit_of_measure", "product_code", "commodity_code", "url",
+                    ]
+                },
+                {"shipping":
+                    [
+                        "first_name", "last_name", "company", "country_code_alpha2", "country_code_alpha3",
+                        "country_code_numeric", "country_name", "extended_address", "locality",
+                        "postal_code", "region", "street_address",
+                    ]
+                },
+            ]
 
     @staticmethod
     def update_details_signature():
@@ -667,10 +686,10 @@ class Transaction(Resource):
 
     @staticmethod
     def refund_signature():
-        return ["amount", "order_id"]
+        return ["amount", "order_id", "merchant_account_id"]
 
     @staticmethod
-    def submit_for_partial_settlement(transaction_id, amount, params={}):
+    def submit_for_partial_settlement(transaction_id, amount, params=None):
         """
         Creates a partial settlement transaction for an authorized transaction
 
@@ -679,24 +698,19 @@ class Transaction(Resource):
             result = braintree.Transaction.submit_for_partial_settlement("my_transaction_id", "20.00")
 
         """
-
+        if params is None:
+            params = {}
         return Configuration.gateway().transaction.submit_for_partial_settlement(transaction_id, amount, params)
 
     def __init__(self, gateway, attributes):
-        if "refund_id" in attributes:
-            self._refund_id = attributes["refund_id"]
-            del(attributes["refund_id"])
-        else:
-            self._refund_id = None
-
         Resource.__init__(self, gateway, attributes)
 
         self.amount = Decimal(self.amount)
-        if "tax_amount" in attributes and self.tax_amount:
+        if "tax_amount" in attributes and getattr(self, "tax_amount", None):
             self.tax_amount = Decimal(self.tax_amount)
-        if "discount_amount" in attributes and self.discount_amount:
+        if "discount_amount" in attributes and getattr(self, "discount_amount", None):
             self.discount_amount = Decimal(self.discount_amount)
-        if "shipping_amount" in attributes and self.shipping_amount:
+        if "shipping_amount" in attributes and getattr(self, "shipping_amount", None):
             self.shipping_amount = Decimal(self.shipping_amount)
         if "billing" in attributes:
             self.billing_details = Address(gateway, attributes.pop("billing"))
@@ -708,30 +722,33 @@ class Transaction(Resource):
             self.paypal_here_details = PayPalHere(gateway, attributes.pop("paypal_here"))
         if "local_payment" in attributes:
             self.local_payment_details = LocalPayment(gateway, attributes.pop("local_payment"))
+        if "sepa_debit_account_detail" in attributes:
+            self.sepa_direct_debit_account_details = SepaDirectDebitAccount(gateway, attributes.pop("sepa_debit_account_detail"))
         if "europe_bank_account" in attributes:
             self.europe_bank_account_details = EuropeBankAccount(gateway, attributes.pop("europe_bank_account"))
         if "us_bank_account" in attributes:
             self.us_bank_account = UsBankAccount(gateway, attributes.pop("us_bank_account"))
-        # NEXT_MAJOR_VERSION Remove this class as legacy Ideal has been removed/disabled in the Braintree Gateway
-        # DEPRECATED If you're looking to accept iDEAL as a payment method contact accounts@braintreepayments.com for a solution.
-        if "ideal_payment" in attributes:
-            self.ideal_payment_details = IdealPayment(gateway, attributes.pop("ideal_payment"))
         if "apple_pay" in attributes:
             self.apple_pay_details = ApplePayCard(gateway, attributes.pop("apple_pay"))
-        if "coinbase_account" in attributes:
-            self.coinbase_details = CoinbaseAccount(gateway, attributes.pop("coinbase_account"))
+        # NEXT_MAJOR_VERSION rename to google_pay_card_details
         if "android_pay_card" in attributes:
             self.android_pay_card_details = AndroidPayCard(gateway, attributes.pop("android_pay_card"))
+        # NEXT_MAJOR_VERSION remove amex express checkout
         if "amex_express_checkout_card" in attributes:
             self.amex_express_checkout_card_details = AmexExpressCheckoutCard(gateway, attributes.pop("amex_express_checkout_card"))
         if "venmo_account" in attributes:
             self.venmo_account_details = VenmoAccount(gateway, attributes.pop("venmo_account"))
         if "visa_checkout_card" in attributes:
             self.visa_checkout_card_details = VisaCheckoutCard(gateway, attributes.pop("visa_checkout_card"))
+        # NEXt_MAJOR_VERSION remove masterpass
         if "masterpass_card" in attributes:
             self.masterpass_card_details = MasterpassCard(gateway, attributes.pop("masterpass_card"))
         if "samsung_pay_card" in attributes:
             self.samsung_pay_card_details = SamsungPayCard(gateway, attributes.pop("samsung_pay_card"))
+        if "sca_exemption_requested" in attributes:
+            self.sca_exemption_requested = attributes.pop("sca_exemption_requested")
+        else:
+            self.sca_exemption_requested = None
         if "customer" in attributes:
             self.customer_details = Customer(gateway, attributes.pop("customer"))
         if "shipping" in attributes:
@@ -754,7 +771,6 @@ class Transaction(Resource):
             self.authorization_adjustments = [AuthorizationAdjustment(authorization_adjustment) for authorization_adjustment in self.authorization_adjustments]
         if "payment_instrument_type" in attributes:
             self.payment_instrument_type = attributes["payment_instrument_type"]
-
         if "risk_data" in attributes:
             self.risk_data = RiskData(attributes["risk_data"])
         else:
@@ -770,11 +786,6 @@ class Transaction(Resource):
         if "network_transaction_id" in attributes:
             self.network_transaction_id = attributes["network_transaction_id"]
 
-    @property
-    def refund_id(self):
-        warnings.warn("Please use Transaction.refund_ids instead", DeprecationWarning)
-        return self._refund_id
-
     @property
     def vault_billing_address(self):
         """
@@ -803,7 +814,7 @@ class Transaction(Resource):
 
     @property
     def is_disbursed(self):
-       return self.disbursement_details.is_valid
+        return self.disbursement_details.is_valid
 
     @property
     def line_items(self):
diff --git a/braintree/transaction_details.py b/braintree/transaction_details.py
index 3002e34..2cdc96a 100644
--- a/braintree/transaction_details.py
+++ b/braintree/transaction_details.py
@@ -5,5 +5,5 @@ class TransactionDetails(AttributeGetter):
     def __init__(self, attributes):
         AttributeGetter.__init__(self, attributes)
 
-        if self.amount is not None:
+        if getattr(self, "amount", None) is not None:
             self.amount = Decimal(self.amount)
diff --git a/braintree/transaction_gateway.py b/braintree/transaction_gateway.py
index ca7e4ce..258a7fa 100644
--- a/braintree/transaction_gateway.py
+++ b/braintree/transaction_gateway.py
@@ -1,18 +1,27 @@
 import braintree
+import warnings
 from braintree.error_result import ErrorResult
 from braintree.resource import Resource
 from braintree.resource_collection import ResourceCollection
 from braintree.successful_result import SuccessfulResult
 from braintree.transaction import Transaction
-from braintree.transparent_redirect import TransparentRedirect
 from braintree.exceptions.not_found_error import NotFoundError
-from braintree.exceptions.down_for_maintenance_error import DownForMaintenanceError
+from braintree.exceptions.request_timeout_error import RequestTimeoutError
+
 
 class TransactionGateway(object):
     def __init__(self, gateway):
         self.gateway = gateway
         self.config = gateway.config
 
+    def adjust_authorization(self, transaction_id, amount):
+        transaction_params = {"amount": amount}
+        response = self.config.http().put(self.config.base_merchant_path() + "/transactions/" + transaction_id + "/adjust_authorization", {"transaction": transaction_params})
+        if "transaction" in response:
+            return SuccessfulResult({"transaction": Transaction(self.gateway, response["transaction"])})
+        elif "api_error_response" in response:
+            return ErrorResult(self.gateway, response["api_error_response"])
+
     def clone_transaction(self, transaction_id, params):
         Resource.verify_keys(params, Transaction.clone_signature())
         return self._post("/transactions/" + transaction_id + "/clone", {"transaction-clone": params})
@@ -24,14 +33,17 @@ class TransactionGateway(object):
         elif "api_error_response" in response:
             return ErrorResult(self.gateway, response["api_error_response"])
 
-    def confirm_transparent_redirect(self, query_string):
-        id = self.gateway.transparent_redirect._parse_and_validate_query_string(query_string)["id"][0]
-        return self._post("/transactions/all/confirm_transparent_redirect_request", {"id": id})
-
     def create(self, params):
         Resource.verify_keys(params, Transaction.create_signature())
+        self.__check_for_deprecated_attributes(params)
         return self._post("/transactions", {"transaction": params})
 
+    def credit(self, params):
+        if params is None:
+            params = {}
+        params["type"] = Transaction.Type.Credit
+        return self.create(params)
+
     def find(self, transaction_id):
         try:
             if transaction_id is None or transaction_id.strip() == "":
@@ -74,6 +86,8 @@ class TransactionGateway(object):
             return ErrorResult(self.gateway, response["api_error_response"])
 
     def sale(self, params):
+        if "recurring" in params.keys():
+            warnings.warn("Use transaction_source parameter instead", DeprecationWarning)
         params.update({"type": "sale"})
         return self.create(params)
 
@@ -85,7 +99,7 @@ class TransactionGateway(object):
         if "search_results" in response:
             return ResourceCollection(query, response, self.__fetch)
         else:
-            raise DownForMaintenanceError("search timeout")
+            raise RequestTimeoutError("search timeout")
 
     def release_from_escrow(self, transaction_id):
         response = self.config.http().put(self.config.base_merchant_path() + "/transactions/" + transaction_id + "/release_from_escrow", {})
@@ -94,7 +108,9 @@ class TransactionGateway(object):
         elif "api_error_response" in response:
             return ErrorResult(self.gateway, response["api_error_response"])
 
-    def submit_for_settlement(self, transaction_id, amount=None, params={}):
+    def submit_for_settlement(self, transaction_id, amount=None, params=None):
+        if params is None:
+            params = {}
         Resource.verify_keys(params, Transaction.submit_for_settlement_signature())
         transaction_params = {"amount": amount}
         transaction_params.update(params)
@@ -105,7 +121,9 @@ class TransactionGateway(object):
         elif "api_error_response" in response:
             return ErrorResult(self.gateway, response["api_error_response"])
 
-    def update_details(self, transaction_id, params={}):
+    def update_details(self, transaction_id, params=None):
+        if params is None:
+            params = {}
         Resource.verify_keys(params, Transaction.update_details_signature())
         response = self.config.http().put(self.config.base_merchant_path() + "/transactions/" + transaction_id + "/update_details",
                 {"transaction": params})
@@ -114,7 +132,9 @@ class TransactionGateway(object):
         elif "api_error_response" in response:
             return ErrorResult(self.gateway, response["api_error_response"])
 
-    def submit_for_partial_settlement(self, transaction_id, amount, params={}):
+    def submit_for_partial_settlement(self, transaction_id, amount, params=None):
+        if params is None:
+            params = {}
         Resource.verify_keys(params, Transaction.submit_for_settlement_signature())
         transaction_params = {"amount": amount}
         transaction_params.update(params)
@@ -125,25 +145,6 @@ class TransactionGateway(object):
         elif "api_error_response" in response:
             return ErrorResult(self.gateway, response["api_error_response"])
 
-    def tr_data_for_credit(self, tr_data, redirect_url):
-        if "transaction" not in tr_data:
-            tr_data["transaction"] = {}
-        tr_data["transaction"]["type"] = Transaction.Type.Credit
-        Resource.verify_keys(tr_data, [{"transaction": Transaction.create_signature()}])
-        tr_data["kind"] = TransparentRedirect.Kind.CreateTransaction
-        return self.gateway.transparent_redirect.tr_data(tr_data, redirect_url)
-
-    def tr_data_for_sale(self, tr_data, redirect_url):
-        if "transaction" not in tr_data:
-            tr_data["transaction"] = {}
-        tr_data["transaction"]["type"] = Transaction.Type.Sale
-        Resource.verify_keys(tr_data, [{"transaction": Transaction.create_signature()}])
-        tr_data["kind"] = TransparentRedirect.Kind.CreateTransaction
-        return self.gateway.transparent_redirect.tr_data(tr_data, redirect_url)
-
-    def transparent_redirect_create_url(self):
-        return self.config.base_url() + self.config.base_merchant_path() + "/transactions/all/create_via_transparent_redirect_request"
-
     def void(self, transaction_id):
         response = self.config.http().put(self.config.base_merchant_path() + "/transactions/" + transaction_id + "/void")
         if "transaction" in response:
@@ -158,7 +159,7 @@ class TransactionGateway(object):
         if "credit_card_transactions" in response:
             return [Transaction(self.gateway, item) for item in ResourceCollection._extract_as_array(response["credit_card_transactions"], "transaction")]
         else:
-            raise DownForMaintenanceError("search timeout")
+            raise RequestTimeoutError("search timeout")
 
     def __criteria(self, query):
         criteria = {}
@@ -169,10 +170,17 @@ class TransactionGateway(object):
                 criteria[term.name] = term.to_param()
         return criteria
 
-    def _post(self, url, params={}):
+    def _post(self, url, params=None):
+        if params is None:
+            params = {}
         response = self.config.http().post(self.config.base_merchant_path() + url, params)
         if "transaction" in response:
             return SuccessfulResult({"transaction": Transaction(self.gateway, response["transaction"])})
         elif "api_error_response" in response:
             return ErrorResult(self.gateway, response["api_error_response"])
 
+    def __check_for_deprecated_attributes(self, params):
+        if "device_session_id" in params.keys():
+            warnings.warn("device_session_id is deprecated, use device_data parameter instead", DeprecationWarning)
+        if "fraud_merchant_id" in params.keys():
+            warnings.warn("fraud_merchant_id is deprecated, use device_data parameter instead", DeprecationWarning)
diff --git a/braintree/transaction_line_item.py b/braintree/transaction_line_item.py
index ce747a6..3e58ce9 100644
--- a/braintree/transaction_line_item.py
+++ b/braintree/transaction_line_item.py
@@ -8,6 +8,7 @@ from braintree.configuration import Configuration
 class TransactionLineItem(AttributeGetter):
     pass
 
+    # NEXT_MAJOR_VERSION this can be an enum! they were added as of python 3.4 and we support 3.5+
     class Kind(object):
         """
         Constants representing transaction line item kinds. Available kinds are:
diff --git a/braintree/transaction_line_item_gateway.py b/braintree/transaction_line_item_gateway.py
index 0c234f2..670ada4 100644
--- a/braintree/transaction_line_item_gateway.py
+++ b/braintree/transaction_line_item_gateway.py
@@ -4,7 +4,7 @@ from braintree.resource import Resource
 from braintree.resource_collection import ResourceCollection
 from braintree.transaction_line_item import TransactionLineItem
 from braintree.exceptions.not_found_error import NotFoundError
-from braintree.exceptions.down_for_maintenance_error import DownForMaintenanceError
+from braintree.exceptions.request_timeout_error import RequestTimeoutError
 
 class TransactionLineItemGateway(object):
     def __init__(self, gateway):
@@ -19,6 +19,6 @@ class TransactionLineItemGateway(object):
             if "line_items" in response:
                 return [TransactionLineItem(item) for item in ResourceCollection._extract_as_array(response, "line_items")]
             else:
-                raise DownForMaintenanceError()
+                raise RequestTimeoutError()
         except NotFoundError:
             raise NotFoundError("transaction line items with id " + repr(transaction_id) + " not found")
diff --git a/braintree/transaction_review.py b/braintree/transaction_review.py
new file mode 100644
index 0000000..8207a74
--- /dev/null
+++ b/braintree/transaction_review.py
@@ -0,0 +1,9 @@
+from braintree.resource import Resource
+
+class TransactionReview(Resource):
+    """
+    A class representing a Transaction Review.
+    """
+
+    def __init__(self, attributes):
+        Resource.__init__(self, None, attributes)
diff --git a/braintree/transaction_search.py b/braintree/transaction_search.py
index 50d6998..649044d 100644
--- a/braintree/transaction_search.py
+++ b/braintree/transaction_search.py
@@ -42,7 +42,9 @@ class TransactionSearch:
     paypal_payer_email           = Search.TextNodeBuilder("paypal_payer_email")
     paypal_payment_id            = Search.TextNodeBuilder("paypal_payment_id")
     paypal_authorization_id      = Search.TextNodeBuilder("paypal_authorization_id")
+    sepa_debit_paypal_v2_order_id = Search.TextNodeBuilder("sepa_debit_paypal_v2_order_id")
     credit_card_unique_identifier = Search.TextNodeBuilder("credit_card_unique_identifier")
+    store_id                     = Search.TextNodeBuilder("store_id")
 
     credit_card_expiration_date  = Search.EqualityNodeBuilder("credit_card_expiration_date")
     credit_card_number           = Search.PartialMatchNodeBuilder("credit_card_number")
@@ -51,6 +53,7 @@ class TransactionSearch:
     ids                          = Search.MultipleValueNodeBuilder("ids")
     merchant_account_id          = Search.MultipleValueNodeBuilder("merchant_account_id")
     payment_instrument_type      = Search.MultipleValueNodeBuilder("payment_instrument_type")
+    store_ids                    = Search.MultipleValueNodeBuilder("store_ids")
 
     created_using = Search.MultipleValueNodeBuilder(
         "created_using",
@@ -93,3 +96,5 @@ class TransactionSearch:
     settled_at = Search.RangeNodeBuilder("settled_at")
     submitted_for_settlement_at = Search.RangeNodeBuilder("submitted_for_settlement_at")
     voided_at = Search.RangeNodeBuilder("voided_at")
+    ach_return_responses_created_at = Search.RangeNodeBuilder("ach_return_responses_created_at")
+    reason_code = Search.MultipleValueNodeBuilder('reason_code')
diff --git a/braintree/transparent_redirect.py b/braintree/transparent_redirect.py
deleted file mode 100644
index bdcbad6..0000000
--- a/braintree/transparent_redirect.py
+++ /dev/null
@@ -1,37 +0,0 @@
-import braintree
-from braintree.configuration import Configuration
-
-class TransparentRedirect:
-    """
-    A class used for Transparent Redirect operations
-    """
-
-    class Kind(object):
-        CreateCustomer = "create_customer"
-        UpdateCustomer = "update_customer"
-        CreatePaymentMethod = "create_payment_method"
-        UpdatePaymentMethod = "update_payment_method"
-        CreateTransaction = "create_transaction"
-
-    @staticmethod
-    def confirm(query_string):
-        """
-        Confirms a transparent redirect request. It expects the query string from the
-        redirect request. The query string should _not_ include the leading "?" character. ::
-
-            result = braintree.TransparentRedirect.confirm("foo=bar&id=12345")
-        """
-        return Configuration.gateway().transparent_redirect.confirm(query_string)
-
-
-    @staticmethod
-    def tr_data(data, redirect_url):
-        return Configuration.gateway().transparent_redirect.tr_data(data, redirect_url)
-
-    @staticmethod
-    def url():
-        """
-        Returns the url for POSTing Transparent Redirect HTML forms
-        """
-        return Configuration.gateway().transparent_redirect.url()
-
diff --git a/braintree/transparent_redirect_gateway.py b/braintree/transparent_redirect_gateway.py
deleted file mode 100644
index ec6dde7..0000000
--- a/braintree/transparent_redirect_gateway.py
+++ /dev/null
@@ -1,79 +0,0 @@
-import cgi
-from datetime import datetime
-import braintree
-from braintree.util.crypto import Crypto
-from braintree.error_result import ErrorResult
-from braintree.exceptions.forged_query_string_error import ForgedQueryStringError
-from braintree.util.http import Http
-from braintree.signature_service import SignatureService
-from braintree.successful_result import SuccessfulResult
-from braintree.transparent_redirect import TransparentRedirect
-
-class TransparentRedirectGateway(object):
-    def __init__(self, gateway):
-        self.gateway = gateway
-        self.config = gateway.config
-
-    def confirm(self, query_string):
-        """
-        Confirms a transparent redirect request. It expects the query string from the
-        redirect request. The query string should _not_ include the leading "?" character. ::
-
-            result = braintree.TransparentRedirect.confirm("foo=bar&id=12345")
-        """
-        parsed_query_string = self._parse_and_validate_query_string(query_string)
-        confirmation_gateway = {
-            TransparentRedirect.Kind.CreateCustomer: "customer",
-            TransparentRedirect.Kind.UpdateCustomer: "customer",
-            TransparentRedirect.Kind.CreatePaymentMethod: "credit_card",
-            TransparentRedirect.Kind.UpdatePaymentMethod: "credit_card",
-            TransparentRedirect.Kind.CreateTransaction: "transaction"
-        }[parsed_query_string["kind"][0]]
-
-        return getattr(self.gateway, confirmation_gateway)._post("/transparent_redirect_requests/" + parsed_query_string["id"][0] + "/confirm")
-
-    def tr_data(self, data, redirect_url):
-        data = self.__flatten_dictionary(data)
-        date_string = datetime.utcnow().strftime("%Y%m%d%H%M%S")
-        data["time"] = date_string
-        data["redirect_url"] = redirect_url
-        data["public_key"] = self.config.public_key
-        data["api_version"] = self.config.api_version()
-
-        return SignatureService(self.config.private_key).sign(data)
-
-    def url(self):
-        """
-        Returns the url for POSTing Transparent Redirect HTML forms
-        """
-        return self.config.base_url() + self.config.base_merchant_path() + "/transparent_redirect_requests"
-
-    def _parse_and_validate_query_string(self, query_string):
-        query_params = cgi.parse_qs(query_string)
-        http_status = int(query_params["http_status"][0])
-        message = query_params.get("bt_message")
-        if message is not None:
-            message = message[0]
-
-        if Http.is_error_status(http_status):
-            Http.raise_exception_from_status(http_status, message)
-
-        if not self._is_valid_tr_query_string(query_string):
-            raise ForgedQueryStringError
-
-        return query_params
-
-    def _is_valid_tr_query_string(self, query_string):
-        content, hash = query_string.split("&hash=")
-        return hash == Crypto.sha1_hmac_hash(self.config.private_key, content)
-
-    def __flatten_dictionary(self, params, parent=None):
-        data = {}
-        for key, val in params.items():
-            full_key = parent + "[" + key + "]" if parent else key
-            if isinstance(val, dict):
-                data.update(self.__flatten_dictionary(val, full_key))
-            else:
-                data[full_key] = val
-        return data
-
diff --git a/braintree/us_bank_account_verification.py b/braintree/us_bank_account_verification.py
index 1a147f1..a727dc9 100644
--- a/braintree/us_bank_account_verification.py
+++ b/braintree/us_bank_account_verification.py
@@ -4,6 +4,7 @@ import braintree.us_bank_account
 
 class UsBankAccountVerification(AttributeGetter):
 
+    # NEXT_MAJOR_VERSION this can be an enum! they were added as of python 3.4 and we support 3.5+
     class Status(object):
         """
         Constants representing transaction statuses. Available statuses are:
@@ -23,6 +24,7 @@ class UsBankAccountVerification(AttributeGetter):
         Verified               = "verified"
         Pending                = "pending"
 
+    # NEXT_MAJOR_VERSION this can be an enum! they were added as of python 3.4 and we support 3.5+
     class VerificationMethod(object):
         """
         Constants representing transaction statuses. Available statuses are:
diff --git a/braintree/us_bank_account_verification_search.py b/braintree/us_bank_account_verification_search.py
index 22d6e4f..c97dd98 100644
--- a/braintree/us_bank_account_verification_search.py
+++ b/braintree/us_bank_account_verification_search.py
@@ -29,5 +29,5 @@ class UsBankAccountVerificationSearch:
     # Equality fields
     account_type = Search.EqualityNodeBuilder("account_type")
 
-    # Ends-with fieds
+    # Ends-with fields
     account_number = Search.EndsWithNodeBuilder("account_number")
diff --git a/braintree/util/constants.py b/braintree/util/constants.py
index 0a3e845..d495c7d 100644
--- a/braintree/util/constants.py
+++ b/braintree/util/constants.py
@@ -1,3 +1,4 @@
+#NEXT_MAJOR_VERSION we can change all Constants to enums, not sure if we'll still need this
 class Constants(object):
     @staticmethod
     def get_all_constant_values_from_class(klass):
diff --git a/braintree/util/crypto.py b/braintree/util/crypto.py
index 24f741d..fa27525 100644
--- a/braintree/util/crypto.py
+++ b/braintree/util/crypto.py
@@ -2,10 +2,7 @@ import hashlib
 import hmac
 import sys
 
-if sys.version_info[0] == 2:
-    text_type = unicode
-else:
-    text_type = str
+text_type = str
 
 class Crypto:
     @staticmethod
diff --git a/braintree/util/generator.py b/braintree/util/generator.py
index fe08a29..895c00c 100644
--- a/braintree/util/generator.py
+++ b/braintree/util/generator.py
@@ -2,14 +2,9 @@ import datetime
 import sys
 from decimal import Decimal
 
-if sys.version_info[0] == 2:
-    integer_types = int, long
-    text_type = unicode
-    binary_type = str
-else:
-    integer_types = int,
-    text_type = str
-    binary_type = bytes
+integer_types = int
+text_type = str
+binary_type = bytes
 
 class Generator(object):
     def __init__(self, dict):
diff --git a/braintree/util/graphql_client.py b/braintree/util/graphql_client.py
index 6b2795a..b5bd615 100644
--- a/braintree/util/graphql_client.py
+++ b/braintree/util/graphql_client.py
@@ -2,7 +2,7 @@ import json
 
 from braintree.exceptions.authentication_error import AuthenticationError
 from braintree.exceptions.authorization_error import AuthorizationError
-from braintree.exceptions.down_for_maintenance_error import DownForMaintenanceError
+from braintree.exceptions.service_unavailable_error import ServiceUnavailableError
 from braintree.exceptions.not_found_error import NotFoundError
 from braintree.exceptions.server_error import ServerError
 from braintree.exceptions.too_many_requests_error import TooManyRequestsError
@@ -35,7 +35,7 @@ class GraphQLClient(Http):
                 elif error_type == "INTERNAL":
                     raise ServerError
                 elif error_type == "SERVICE_AVAILABILITY":
-                    raise DownForMaintenanceError
+                    raise ServiceUnavailableError
                 else:
                     raise UnexpectedError("Unexpected Response: " + error["message"])
 
diff --git a/braintree/util/http.py b/braintree/util/http.py
index eb2924d..a24480d 100644
--- a/braintree/util/http.py
+++ b/braintree/util/http.py
@@ -1,9 +1,6 @@
 import sys
 import requests
-if sys.version_info[0] == 2:
-    from base64 import encodestring as encodebytes
-else:
-    from base64 import encodebytes
+from base64 import encodebytes
 import json
 import braintree
 from braintree import version
@@ -11,17 +8,19 @@ from braintree.environment import Environment
 from braintree.util.xml_util import XmlUtil
 from braintree.exceptions.authentication_error import AuthenticationError
 from braintree.exceptions.authorization_error import AuthorizationError
-from braintree.exceptions.down_for_maintenance_error import DownForMaintenanceError
-from braintree.exceptions.not_found_error import NotFoundError
-from braintree.exceptions.server_error import ServerError
-from braintree.exceptions.too_many_requests_error import TooManyRequestsError
-from braintree.exceptions.upgrade_required_error import UpgradeRequiredError
-from braintree.exceptions.unexpected_error import UnexpectedError
+from braintree.exceptions.gateway_timeout_error import GatewayTimeoutError
 from braintree.exceptions.http.connection_error import ConnectionError
 from braintree.exceptions.http.invalid_response_error import InvalidResponseError
-from braintree.exceptions.http.timeout_error import TimeoutError
 from braintree.exceptions.http.timeout_error import ConnectTimeoutError
 from braintree.exceptions.http.timeout_error import ReadTimeoutError
+from braintree.exceptions.http.timeout_error import TimeoutError
+from braintree.exceptions.not_found_error import NotFoundError
+from braintree.exceptions.request_timeout_error import RequestTimeoutError
+from braintree.exceptions.server_error import ServerError
+from braintree.exceptions.service_unavailable_error import ServiceUnavailableError
+from braintree.exceptions.too_many_requests_error import TooManyRequestsError
+from braintree.exceptions.unexpected_error import UnexpectedError
+from braintree.exceptions.upgrade_required_error import UpgradeRequiredError
 
 class Http(object):
     class ContentType(object):
@@ -31,7 +30,7 @@ class Http(object):
 
     @staticmethod
     def is_error_status(status):
-        return status not in [200, 201, 422]
+        return status not in [200, 201, 204, 422]
 
     @staticmethod
     def raise_exception_from_status(status, message=None):
@@ -41,6 +40,8 @@ class Http(object):
             raise AuthorizationError(message)
         elif status == 404:
             raise NotFoundError()
+        elif status == 408:
+            raise RequestTimeoutError()
         elif status == 426:
             raise UpgradeRequiredError()
         elif status == 429:
@@ -48,7 +49,9 @@ class Http(object):
         elif status == 500:
             raise ServerError()
         elif status == 503:
-            raise DownForMaintenanceError()
+            raise ServiceUnavailableError()
+        elif status == 504:
+            raise GatewayTimeoutError()
         else:
             raise UnexpectedError("Unexpected HTTP_RESPONSE " + str(status))
 
@@ -75,8 +78,7 @@ class Http(object):
         http_strategy = self.config.http_strategy()
         headers = self.__headers(content_type, header_overrides)
         request_body = self.__request_body(content_type, params, files)
-
-        full_path = path if path.startswith(self.config.base_url()) or path.startswith(self.config.graphql_base_url()) else (self.config.base_url() + path)
+        full_path = self.__full_path(path)
 
         try:
             status, response_body = http_strategy.http_do(http_verb, full_path, headers, request_body)
@@ -100,6 +102,7 @@ class Http(object):
     def http_do(self, http_verb, path, headers, request_body):
         data = request_body
         files = None
+        full_path = self.__full_path(path)
 
         if type(request_body) is tuple:
             data = request_body[0]
@@ -110,14 +113,19 @@ class Http(object):
         else:
           verify = self.environment.ssl_certificate
 
-        response = self.__request_function(http_verb)(
-            path if path.startswith(self.config.base_url()) or path.startswith(self.config.graphql_base_url()) else (self.config.base_url() + path),
-            headers=headers,
-            data=data,
-            files=files,
-            verify=verify,
-            timeout=self.config.timeout
-        )
+        with requests.Session() as session:
+            request = requests.Request(
+                method=http_verb,
+                url=full_path,
+                headers=headers,
+                data=data,
+                files=files)
+            prepared_request = request.prepare()
+            prepared_request.url = full_path
+
+            response = session.send(prepared_request,
+                verify=verify,
+                timeout=self.config.timeout)
 
         return [response.status_code, response.text]
 
@@ -135,16 +143,6 @@ class Http(object):
         else:
             raise UnexpectedError(exception)
 
-    def __request_function(self, method):
-        if method == "GET":
-            return requests.get
-        elif method == "POST":
-            return requests.post
-        elif method == "PUT":
-            return requests.put
-        elif method == "DELETE":
-            return requests.delete
-
     def __authorization_header(self):
         if self.config.has_client_credentials():
             return b"Basic " + encodebytes(
@@ -185,3 +183,7 @@ class Http(object):
             return params
         else:
             return (params, files)
+
+    def __full_path(self, path):
+        return path if path.startswith(self.config.base_url()) or path.startswith(self.config.graphql_base_url()) else (self.config.base_url() + path)
+
diff --git a/braintree/util/parser.py b/braintree/util/parser.py
index 2dd7df2..1bbd0fb 100644
--- a/braintree/util/parser.py
+++ b/braintree/util/parser.py
@@ -4,16 +4,13 @@ from braintree.util.datetime_parser import parse_datetime
 import re
 import sys
 
-if sys.version_info[0] == 2:
-    binary_type = str
-else:
-    binary_type = bytes
+binary_type = bytes
 
 class Parser(object):
     def __init__(self, xml):
         if isinstance(xml, binary_type):
             xml = xml.decode('utf-8')
-        self.doc = minidom.parseString("><".join(re.split(">\s+<", xml)).strip())
+        self.doc = minidom.parseString("><".join(re.split(r">\s+<", xml)).strip())
 
     def parse(self):
         return {self.__underscored(self.doc.documentElement.tagName): self.__parse_node(self.doc.documentElement)}
diff --git a/braintree/validation_error_collection.py b/braintree/validation_error_collection.py
index 6b4f266..5235dc2 100644
--- a/braintree/validation_error_collection.py
+++ b/braintree/validation_error_collection.py
@@ -1,14 +1,17 @@
 from braintree.validation_error import ValidationError
 
+
 class ValidationErrorCollection(object):
     """
     A class representing a collection of validation errors.
 
-    For more information on ValidationErrors, see https://developers.braintreepayments.com/reference/general/validation-errors/overview/python
+    For more information on ValidationErrors, see https://developer.paypal.com/braintree/docs/reference/general/validation-errors/overview/python
 
     """
 
-    def __init__(self, data={"errors": []}):
+    def __init__(self, data=None):
+        if data is None:
+            data = {"errors": []}
         self.data = data
 
     @property
@@ -87,7 +90,7 @@ class ValidationErrorCollection(object):
     def __nested_errors(self):
         nested_errors = {}
         for key in self.data:
-            if key == "errors": continue
+            if key == "errors":
+                continue
             nested_errors[key] = ValidationErrorCollection(self.data[key])
         return nested_errors
-
diff --git a/braintree/venmo_profile_data.py b/braintree/venmo_profile_data.py
new file mode 100644
index 0000000..64ccd90
--- /dev/null
+++ b/braintree/venmo_profile_data.py
@@ -0,0 +1,9 @@
+from braintree.resource import Resource
+
+class VenmoProfileData(Resource):
+    """
+    A class representing Braintree VenmoProfileData object.
+    """
+    def __init__(self, gateway, attributes):
+        Resource.__init__(self, gateway, attributes)
+
diff --git a/braintree/version.py b/braintree/version.py
index a681e20..0e6dbf6 100644
--- a/braintree/version.py
+++ b/braintree/version.py
@@ -1 +1 @@
-Version = "3.57.1"
+Version = "4.18.0"
diff --git a/braintree/webhook_notification.py b/braintree/webhook_notification.py
index ed909d7..7797683 100644
--- a/braintree/webhook_notification.py
+++ b/braintree/webhook_notification.py
@@ -1,34 +1,56 @@
-from braintree.resource import Resource
+from braintree.account_updater_daily_report import AccountUpdaterDailyReport
 from braintree.configuration import Configuration
-from braintree.subscription import Subscription
-from braintree.merchant_account import MerchantAccount
-from braintree.transaction import Transaction
-from braintree.partner_merchant import PartnerMerchant
-from braintree.oauth_access_revocation import OAuthAccessRevocation
+from braintree.connected_merchant_paypal_status_changed import ConnectedMerchantPayPalStatusChanged
+from braintree.connected_merchant_status_transitioned import ConnectedMerchantStatusTransitioned
 from braintree.disbursement import Disbursement
 from braintree.dispute import Dispute
-from braintree.account_updater_daily_report import AccountUpdaterDailyReport
 from braintree.error_result import ErrorResult
-from braintree.validation_error_collection import ValidationErrorCollection
-from braintree.connected_merchant_paypal_status_changed import ConnectedMerchantPayPalStatusChanged
-from braintree.connected_merchant_status_transitioned import ConnectedMerchantStatusTransitioned
-# NEXT_MAJOR_VERSION Remove this class as legacy Ideal has been removed/disabled in the Braintree Gateway
-# DEPRECATED If you're looking to accept iDEAL as a payment method contact accounts@braintreepayments.com for a solution.
-from braintree.ideal_payment import IdealPayment
 from braintree.granted_payment_instrument_update import GrantedPaymentInstrumentUpdate
-from braintree.revoked_payment_method_metadata import RevokedPaymentMethodMetadata
 from braintree.local_payment_completed import LocalPaymentCompleted
+from braintree.local_payment_expired import LocalPaymentExpired
+from braintree.local_payment_funded import LocalPaymentFunded
+from braintree.local_payment_reversed import LocalPaymentReversed
+from braintree.merchant_account import MerchantAccount
+from braintree.oauth_access_revocation import OAuthAccessRevocation
+from braintree.partner_merchant import PartnerMerchant
+from braintree.payment_method_customer_data_updated_metadata import PaymentMethodCustomerDataUpdatedMetadata
+from braintree.resource import Resource
+from braintree.revoked_payment_method_metadata import RevokedPaymentMethodMetadata
+from braintree.subscription import Subscription
+from braintree.transaction import Transaction
+from braintree.transaction_review import TransactionReview
+from braintree.validation_error_collection import ValidationErrorCollection
 
 class WebhookNotification(Resource):
     class Kind(object):
         AccountUpdaterDailyReport = "account_updater_daily_report"
         Check = "check"
-        ConnectedMerchantStatusTransitioned = "connected_merchant_status_transitioned"
         ConnectedMerchantPayPalStatusChanged = "connected_merchant_paypal_status_changed"
+        ConnectedMerchantStatusTransitioned = "connected_merchant_status_transitioned"
+        Disbursement = "disbursement"
+        DisbursementException = "disbursement_exception"
+        DisputeAccepted = "dispute_accepted"
+        DisputeAutoAccepted = "dispute_auto_accepted"
+        DisputeDisputed = "dispute_disputed"
+        DisputeExpired = "dispute_expired"
+        DisputeLost = "dispute_lost"
+        DisputeOpened = "dispute_opened"
+        DisputeWon = "dispute_won"
+        GrantedPaymentMethodRevoked = "granted_payment_method_revoked"
+        GrantorUpdatedGrantedPaymentMethod = "grantor_updated_granted_payment_method"
+        LocalPaymentCompleted = "local_payment_completed"
+        LocalPaymentExpired = "local_payment_expired"
+        LocalPaymentFunded = "local_payment_funded"
+        LocalPaymentReversed = "local_payment_reversed"
+        OAuthAccessRevoked = "oauth_access_revoked"
         PartnerMerchantConnected = "partner_merchant_connected"
-        PartnerMerchantDisconnected = "partner_merchant_disconnected"
         PartnerMerchantDeclined = "partner_merchant_declined"
-        OAuthAccessRevoked = "oauth_access_revoked"
+        PartnerMerchantDisconnected = "partner_merchant_disconnected"
+        PaymentMethodCustomerDataUpdated = "payment_method_customer_data_updated"
+        PaymentMethodRevokedByCustomer = "payment_method_revoked_by_customer"
+        RecipientUpdatedGrantedPaymentMethod = "recipient_updated_granted_payment_method"
+        SubMerchantAccountApproved = "sub_merchant_account_approved"
+        SubMerchantAccountDeclined = "sub_merchant_account_declined"
         SubscriptionCanceled = "subscription_canceled"
         SubscriptionChargedSuccessfully = "subscription_charged_successfully"
         SubscriptionChargedUnsuccessfully = "subscription_charged_unsuccessfully"
@@ -36,28 +58,10 @@ class WebhookNotification(Resource):
         SubscriptionTrialEnded = "subscription_trial_ended"
         SubscriptionWentActive = "subscription_went_active"
         SubscriptionWentPastDue = "subscription_went_past_due"
-        SubMerchantAccountApproved = "sub_merchant_account_approved"
-        SubMerchantAccountDeclined = "sub_merchant_account_declined"
         TransactionDisbursed = "transaction_disbursed"
+        TransactionReviewed = "transaction_reviewed"
         TransactionSettled = "transaction_settled"
         TransactionSettlementDeclined = "transaction_settlement_declined"
-        DisbursementException = "disbursement_exception"
-        Disbursement = "disbursement"
-        DisputeOpened = "dispute_opened"
-        DisputeLost = "dispute_lost"
-        DisputeWon = "dispute_won"
-        # NEXT_MAJOR_VERSION Remove this class as legacy Ideal has been removed/disabled in the Braintree Gateway
-        # DEPRECATED If you're looking to accept iDEAL as a payment method contact accounts@braintreepayments.com for a solution.
-        IdealPaymentComplete = "ideal_payment_complete"
-        IdealPaymentFailed = "ideal_payment_failed"
-        # NEXT_MAJOR_VERSION remove GrantedPaymentInstrumentUpdate. Kind is not sent by Braintree Gateway.
-        # Kind will either be GrantorUpdatedGrantedPaymentMethod or RecipientUpdatedGrantedPaymentMethod.
-        GrantedPaymentInstrumentUpdate = "granted_payment_instrument_update"
-        GrantorUpdatedGrantedPaymentMethod = "grantor_updated_granted_payment_method"
-        RecipientUpdatedGrantedPaymentMethod = "recipient_updated_granted_payment_method"
-        GrantedPaymentMethodRevoked = "granted_payment_method_revoked"
-        PaymentMethodRevokedByCustomer = "payment_method_revoked_by_customer"
-        LocalPaymentCompleted = "local_payment_completed"
 
     @staticmethod
     def parse(signature, payload):
@@ -84,6 +88,8 @@ class WebhookNotification(Resource):
             self.merchant_account = MerchantAccount(gateway, node_wrapper['merchant_account'])
         elif "transaction" in node_wrapper:
             self.transaction = Transaction(gateway, node_wrapper['transaction'])
+        elif "transaction_review" in node_wrapper:
+            self.transaction_review = TransactionReview(node_wrapper['transaction_review'])
         elif "connected_merchant_status_transitioned" in node_wrapper:
             self.connected_merchant_status_transitioned = ConnectedMerchantStatusTransitioned(gateway, node_wrapper['connected_merchant_status_transitioned'])
         elif "connected_merchant_paypal_status_changed" in node_wrapper:
@@ -98,16 +104,20 @@ class WebhookNotification(Resource):
             self.dispute = Dispute(node_wrapper['dispute'])
         elif "account_updater_daily_report" in node_wrapper:
             self.account_updater_daily_report = AccountUpdaterDailyReport(gateway, node_wrapper['account_updater_daily_report'])
-        # NEXT_MAJOR_VERSION Remove this class as legacy Ideal has been removed/disabled in the Braintree Gateway
-        # DEPRECATED If you're looking to accept iDEAL as a payment method contact accounts@braintreepayments.com for a solution.
-        elif "ideal_payment" in node_wrapper:
-            self.ideal_payment = IdealPayment(gateway, node_wrapper['ideal_payment'])
         elif "granted_payment_instrument_update" in node_wrapper:
             self.granted_payment_instrument_update = GrantedPaymentInstrumentUpdate(gateway, node_wrapper["granted_payment_instrument_update"])
         elif attributes["kind"] in [WebhookNotification.Kind.GrantedPaymentMethodRevoked, WebhookNotification.Kind.PaymentMethodRevokedByCustomer]:
             self.revoked_payment_method_metadata = RevokedPaymentMethodMetadata(gateway, node_wrapper)
-        elif "local_payment" in node_wrapper:
+        elif "local_payment" in node_wrapper and attributes["kind"] == WebhookNotification.Kind.LocalPaymentCompleted:
             self.local_payment_completed = LocalPaymentCompleted(gateway, node_wrapper["local_payment"])
+        elif "local_payment_expired" in node_wrapper and attributes["kind"] == WebhookNotification.Kind.LocalPaymentExpired:
+            self.local_payment_expired = LocalPaymentExpired(gateway, node_wrapper["local_payment_expired"])
+        elif "local_payment_funded" in node_wrapper and attributes["kind"] == WebhookNotification.Kind.LocalPaymentFunded:
+            self.local_payment_funded = LocalPaymentFunded(gateway, node_wrapper["local_payment_funded"])
+        elif "local_payment_reversed" in node_wrapper and attributes["kind"] == WebhookNotification.Kind.LocalPaymentReversed:
+            self.local_payment_reversed = LocalPaymentReversed(gateway, node_wrapper["local_payment_reversed"])
+        elif "payment_method_customer_data_updated_metadata" in node_wrapper and attributes["kind"] == WebhookNotification.Kind.PaymentMethodCustomerDataUpdated:
+            self.payment_method_customer_data_updated_metadata = PaymentMethodCustomerDataUpdatedMetadata(gateway, node_wrapper["payment_method_customer_data_updated_metadata"])
 
         if "errors" in node_wrapper:
             self.errors = ValidationErrorCollection(node_wrapper['errors'])
diff --git a/braintree/webhook_notification_gateway.py b/braintree/webhook_notification_gateway.py
index 5e2ca99..20fb297 100644
--- a/braintree/webhook_notification_gateway.py
+++ b/braintree/webhook_notification_gateway.py
@@ -1,9 +1,6 @@
 import re
 import sys
-if sys.version_info[0] == 2:
-    from base64 import decodestring as decodebytes
-else:
-    from base64 import decodebytes
+from base64 import decodebytes
 import sys
 from braintree.exceptions.invalid_signature_error import InvalidSignatureError
 from braintree.exceptions.invalid_challenge_error import InvalidChallengeError
@@ -11,10 +8,7 @@ from braintree.util.crypto import Crypto
 from braintree.util.xml_util import XmlUtil
 from braintree.webhook_notification import WebhookNotification
 
-if sys.version_info[0] == 2:
-    text_type = unicode
-else:
-    text_type = str
+text_type = str
 
 class WebhookNotificationGateway(object):
     def __init__(self, gateway):
diff --git a/braintree/webhook_testing_gateway.py b/braintree/webhook_testing_gateway.py
index f715a4a..e0ec4ed 100644
--- a/braintree/webhook_testing_gateway.py
+++ b/braintree/webhook_testing_gateway.py
@@ -1,10 +1,7 @@
 from braintree.util.crypto import Crypto
 from braintree.webhook_notification import WebhookNotification
 import sys
-if sys.version_info[0] == 2:
-    from base64 import encodestring as encodebytes
-else:
-    from base64 import encodebytes
+from base64 import encodebytes
 from datetime import datetime
 
 class WebhookTestingGateway(object):
@@ -48,6 +45,8 @@ class WebhookTestingGateway(object):
             return self.__merchant_account_declined_sample_xml(id)
         elif kind == WebhookNotification.Kind.TransactionDisbursed:
             return self.__transaction_disbursed_sample_xml(id)
+        elif kind == WebhookNotification.Kind.TransactionReviewed:
+            return self.__transaction_reviewed_sample_xml(id)
         elif kind == WebhookNotification.Kind.TransactionSettled:
             return self.__transaction_settled_sample_xml(id)
         elif kind == WebhookNotification.Kind.TransactionSettlementDeclined:
@@ -70,31 +69,38 @@ class WebhookTestingGateway(object):
             return self.__dispute_lost_sample_xml(id)
         elif kind == WebhookNotification.Kind.DisputeWon:
             return self.__dispute_won_sample_xml(id)
+        elif kind == WebhookNotification.Kind.DisputeAccepted:
+            return self.__dispute_accepted_sample_xml(id)
+        elif kind == WebhookNotification.Kind.DisputeAutoAccepted:
+            return self.__dispute_auto_accepted_sample_xml(id)
+        elif kind == WebhookNotification.Kind.DisputeDisputed:
+            return self.__dispute_disputed_sample_xml(id)
+        elif kind == WebhookNotification.Kind.DisputeExpired:
+            return self.__dispute_expired_sample_xml(id)
         elif kind == WebhookNotification.Kind.SubscriptionChargedSuccessfully:
             return self.__subscription_charged_successfully_sample_xml(id)
         elif kind == WebhookNotification.Kind.SubscriptionChargedUnsuccessfully:
             return self.__subscription_charged_unsuccessfully_sample_xml(id)
         elif kind == WebhookNotification.Kind.AccountUpdaterDailyReport:
             return self.__account_updater_daily_report_sample_xml()
-        # NEXT_MAJOR_VERSION Remove this class as legacy Ideal has been removed/disabled in the Braintree Gateway
-        # DEPRECATED If you're looking to accept iDEAL as a payment method contact accounts@braintreepayments.com for a solution.
-        elif kind == WebhookNotification.Kind.IdealPaymentComplete:
-            return self.__ideal_payment_complete_sample_xml(id)
-        # NEXT_MAJOR_VERSION Remove this class as legacy Ideal has been removed/disabled in the Braintree Gateway
-        # DEPRECATED If you're looking to accept iDEAL as a payment method contact accounts@braintreepayments.com for a solution.
-        elif kind == WebhookNotification.Kind.IdealPaymentFailed:
-            return self.__ideal_payment_failed_sample_xml(id)
-        # NEXT_MAJOR_VERSION remove GrantedPaymentInstrumentUpdate
-        elif kind == WebhookNotification.Kind.GrantedPaymentInstrumentUpdate:
-            return self.__granted_payment_instrument_update()
         elif kind == WebhookNotification.Kind.GrantorUpdatedGrantedPaymentMethod:
             return self.__granted_payment_instrument_update()
         elif kind == WebhookNotification.Kind.RecipientUpdatedGrantedPaymentMethod:
             return self.__granted_payment_instrument_update()
         elif kind == WebhookNotification.Kind.PaymentMethodRevokedByCustomer:
             return self.__payment_method_revoked_by_customer(id)
+        elif kind == WebhookNotification.Kind.GrantedPaymentMethodRevoked:
+            return self.__granted_payment_method_revoked(id)
         elif kind == WebhookNotification.Kind.LocalPaymentCompleted:
             return self.__local_payment_completed()
+        elif kind == WebhookNotification.Kind.LocalPaymentExpired:
+            return self.__local_payment_expired()
+        elif kind == WebhookNotification.Kind.LocalPaymentFunded:
+            return self.__local_payment_funded()
+        elif kind == WebhookNotification.Kind.LocalPaymentReversed:
+            return self.__local_payment_reversed()
+        elif kind == WebhookNotification.Kind.PaymentMethodCustomerDataUpdated:
+            return self.__payment_method_customer_data_updated_sample_xml(id)
         else:
             return self.__subscription_sample_xml(id)
 
@@ -119,6 +125,17 @@ class WebhookTestingGateway(object):
             </transaction>
         """ % id
 
+    def __transaction_reviewed_sample_xml(self, id):
+        return """
+            <transaction-review>
+              <transaction-id>%s</transaction-id>
+              <decision>a smart decision</decision>
+              <reviewer-email>hey@girl.com</reviewer-email>
+              <reviewer-note>I reviewed this</reviewer-note>
+              <reviewed-time type="datetime">2021-04-20T06:09:00Z</reviewed-time>
+            </transaction-review>
+        """ % id
+
     def __transaction_settled_sample_xml(self, id):
         return """
             <transaction>
@@ -223,6 +240,30 @@ class WebhookTestingGateway(object):
         else:
             return self.__new_dispute_won_sample_xml(id)
 
+    def __dispute_accepted_sample_xml(self, id):
+        if id == "legacy_dispute_id":
+            return self.__old_dispute_accepted_sample_xml(id)
+        else:
+            return self.__new_dispute_accepted_sample_xml(id)
+
+    def __dispute_auto_accepted_sample_xml(self, id):
+        if id == "legacy_dispute_id":
+            return self.__old_dispute_auto_accepted_sample_xml(id)
+        else:
+            return self.__new_dispute_auto_accepted_sample_xml(id)
+
+    def __dispute_disputed_sample_xml(self, id):
+        if id == "legacy_dispute_id":
+            return self.__old_dispute_disputed_sample_xml(id)
+        else:
+            return self.__new_dispute_disputed_sample_xml(id)
+
+    def __dispute_expired_sample_xml(self, id):
+        if id == "legacy_dispute_id":
+            return self.__old_dispute_expired_sample_xml(id)
+        else:
+            return self.__new_dispute_expired_sample_xml(id)
+
     def __old_dispute_opened_sample_xml(self, id):
         return """
             <dispute>
@@ -281,6 +322,82 @@ class WebhookTestingGateway(object):
             </dispute>
         """ % (id, id)
 
+    def __old_dispute_accepted_sample_xml(self, id):
+        return """
+            <dispute>
+              <amount>250.00</amount>
+              <currency-iso-code>USD</currency-iso-code>
+              <received-date type="date">2014-03-01</received-date>
+              <reply-by-date type="date">2014-03-21</reply-by-date>
+              <kind>chargeback</kind>
+              <status>accepted</status>
+              <reason>fraud</reason>
+              <id>%s</id>
+              <transaction>
+                <id>%s</id>
+                <amount>250.00</amount>
+              </transaction>
+              <date-opened type="date">2014-03-28</date-opened>
+            </dispute>
+        """ % (id, id)
+
+    def __old_dispute_auto_accepted_sample_xml(self, id):
+        return """
+            <dispute>
+              <amount>250.00</amount>
+              <currency-iso-code>USD</currency-iso-code>
+              <received-date type="date">2014-03-01</received-date>
+              <reply-by-date type="date">2014-03-21</reply-by-date>
+              <kind>chargeback</kind>
+              <status>auto_accepted</status>
+              <reason>fraud</reason>
+              <id>%s</id>
+              <transaction>
+                <id>%s</id>
+                <amount>250.00</amount>
+              </transaction>
+              <date-opened type="date">2014-03-28</date-opened>
+            </dispute>
+        """ % (id, id)
+
+    def __old_dispute_disputed_sample_xml(self, id):
+        return """
+            <dispute>
+              <amount>250.00</amount>
+              <currency-iso-code>USD</currency-iso-code>
+              <received-date type="date">2014-03-01</received-date>
+              <reply-by-date type="date">2014-03-21</reply-by-date>
+              <kind>chargeback</kind>
+              <status>disputed</status>
+              <reason>fraud</reason>
+              <id>%s</id>
+              <transaction>
+                <id>%s</id>
+                <amount>250.00</amount>
+              </transaction>
+              <date-opened type="date">2014-03-28</date-opened>
+            </dispute>
+        """ % (id, id)
+
+    def __old_dispute_expired_sample_xml(self, id):
+        return """
+            <dispute>
+              <amount>250.00</amount>
+              <currency-iso-code>USD</currency-iso-code>
+              <received-date type="date">2014-03-01</received-date>
+              <reply-by-date type="date">2014-03-21</reply-by-date>
+              <kind>chargeback</kind>
+              <status>expired</status>
+              <reason>fraud</reason>
+              <id>%s</id>
+              <transaction>
+                <id>%s</id>
+                <amount>250.00</amount>
+              </transaction>
+              <date-opened type="date">2014-03-28</date-opened>
+            </dispute>
+        """ % (id, id)
+
     def __new_dispute_opened_sample_xml(self, id):
         return """
         <dispute>
@@ -443,6 +560,186 @@ class WebhookTestingGateway(object):
         </dispute>
         """ % (id, id)
 
+    def __new_dispute_accepted_sample_xml(self, id):
+        return """
+        <dispute>
+          <id>%s</id>
+          <amount>100.00</amount>
+          <amount-disputed>100.00</amount-disputed>
+          <amount-won>95.00</amount-won>
+          <case-number>CASE-12345</case-number>
+          <created-at type="datetime">2017-06-16T20:44:41Z</created-at>
+          <currency-iso-code>USD</currency-iso-code>
+          <forwarded-comments nil="true"/>
+          <kind>chargeback</kind>
+          <merchant-account-id>ytnlulaloidoqwvzxjrdqputg</merchant-account-id>
+          <reason>fraud</reason>
+          <reason-code nil="true"/>
+          <reason-description nil="true"/>
+          <received-date type="date">2016-02-15</received-date>
+          <reference-number>REF-9876</reference-number>
+          <reply-by-date type="date">2016-02-22</reply-by-date>
+          <status>accepted</status>
+          <updated-at type="datetime">2017-06-16T20:44:41Z</updated-at>
+          <original-dispute-id>9qde5qgp</original-dispute-id>
+          <status-history type="array">
+            <status-history>
+              <status>open</status>
+              <timestamp type="datetime">2017-06-15T20:44:41Z</timestamp>
+            </status-history>
+            <status-history>
+              <status>accepted</status>
+              <timestamp type="datetime">2017-06-16T20:44:41Z</timestamp>
+            </status-history>
+          </status-history>
+          <evidence type="array"/>
+          <transaction>
+            <id>%s</id>
+            <amount>100.00</amount>
+            <created-at>2017-06-21T20:44:41Z</created-at>
+            <order-id nil="true"/>
+            <purchase-order-number nil="true"/>
+            <payment-instrument-subtype>Visa</payment-instrument-subtype>
+          </transaction>
+          <date-opened type=\"date\">2014-03-28</date-opened>
+        </dispute>
+        """ % (id, id)
+
+    def __new_dispute_auto_accepted_sample_xml(self, id):
+        return """
+        <dispute>
+          <id>%s</id>
+          <amount>100.00</amount>
+          <amount-disputed>100.00</amount-disputed>
+          <amount-won>95.00</amount-won>
+          <case-number>CASE-12345</case-number>
+          <created-at type="datetime">2017-06-16T20:44:41Z</created-at>
+          <currency-iso-code>USD</currency-iso-code>
+          <forwarded-comments nil="true"/>
+          <kind>chargeback</kind>
+          <merchant-account-id>ytnlulaloidoqwvzxjrdqputg</merchant-account-id>
+          <reason>fraud</reason>
+          <reason-code nil="true"/>
+          <reason-description nil="true"/>
+          <received-date type="date">2016-02-15</received-date>
+          <reference-number>REF-9876</reference-number>
+          <reply-by-date type="date">2016-02-22</reply-by-date>
+          <status>auto_accepted</status>
+          <updated-at type="datetime">2017-06-16T20:44:41Z</updated-at>
+          <original-dispute-id>9qde5qgp</original-dispute-id>
+          <status-history type="array">
+            <status-history>
+              <status>open</status>
+              <timestamp type="datetime">2017-06-15T20:44:41Z</timestamp>
+            </status-history>
+            <status-history>
+              <status>auto_accepted</status>
+              <timestamp type="datetime">2017-06-16T20:44:41Z</timestamp>
+            </status-history>
+          </status-history>
+          <evidence type="array"/>
+          <transaction>
+            <id>%s</id>
+            <amount>100.00</amount>
+            <created-at>2017-06-21T20:44:41Z</created-at>
+            <order-id nil="true"/>
+            <purchase-order-number nil="true"/>
+            <payment-instrument-subtype>Visa</payment-instrument-subtype>
+          </transaction>
+          <date-opened type=\"date\">2014-03-28</date-opened>
+        </dispute>
+        """ % (id, id)
+
+    def __new_dispute_disputed_sample_xml(self, id):
+        return """
+        <dispute>
+          <id>%s</id>
+          <amount>100.00</amount>
+          <amount-disputed>100.00</amount-disputed>
+          <amount-won>95.00</amount-won>
+          <case-number>CASE-12345</case-number>
+          <created-at type="datetime">2017-06-16T20:44:41Z</created-at>
+          <currency-iso-code>USD</currency-iso-code>
+          <forwarded-comments nil="true"/>
+          <kind>chargeback</kind>
+          <merchant-account-id>ytnlulaloidoqwvzxjrdqputg</merchant-account-id>
+          <reason>fraud</reason>
+          <reason-code nil="true"/>
+          <reason-description nil="true"/>
+          <received-date type="date">2016-02-15</received-date>
+          <reference-number>REF-9876</reference-number>
+          <reply-by-date type="date">2016-02-22</reply-by-date>
+          <status>disputed</status>
+          <updated-at type="datetime">2017-06-16T20:44:41Z</updated-at>
+          <original-dispute-id>9qde5qgp</original-dispute-id>
+          <status-history type="array">
+            <status-history>
+              <status>open</status>
+              <timestamp type="datetime">2017-06-15T20:44:41Z</timestamp>
+            </status-history>
+            <status-history>
+              <status>disputed</status>
+              <timestamp type="datetime">2017-06-16T20:44:41Z</timestamp>
+            </status-history>
+          </status-history>
+          <evidence type="array"/>
+          <transaction>
+            <id>%s</id>
+            <amount>100.00</amount>
+            <created-at>2017-06-21T20:44:41Z</created-at>
+            <order-id nil="true"/>
+            <purchase-order-number nil="true"/>
+            <payment-instrument-subtype>Visa</payment-instrument-subtype>
+          </transaction>
+          <date-opened type=\"date\">2014-03-28</date-opened>
+        </dispute>
+        """ % (id, id)
+
+    def __new_dispute_expired_sample_xml(self, id):
+        return """
+        <dispute>
+          <id>%s</id>
+          <amount>100.00</amount>
+          <amount-disputed>100.00</amount-disputed>
+          <amount-won>95.00</amount-won>
+          <case-number>CASE-12345</case-number>
+          <created-at type="datetime">2017-06-16T20:44:41Z</created-at>
+          <currency-iso-code>USD</currency-iso-code>
+          <forwarded-comments nil="true"/>
+          <kind>chargeback</kind>
+          <merchant-account-id>ytnlulaloidoqwvzxjrdqputg</merchant-account-id>
+          <reason>fraud</reason>
+          <reason-code nil="true"/>
+          <reason-description nil="true"/>
+          <received-date type="date">2016-02-15</received-date>
+          <reference-number>REF-9876</reference-number>
+          <reply-by-date type="date">2016-02-22</reply-by-date>
+          <status>expired</status>
+          <updated-at type="datetime">2017-06-16T20:44:41Z</updated-at>
+          <original-dispute-id>9qde5qgp</original-dispute-id>
+          <status-history type="array">
+            <status-history>
+              <status>open</status>
+              <timestamp type="datetime">2017-06-15T20:44:41Z</timestamp>
+            </status-history>
+            <status-history>
+              <status>expired</status>
+              <timestamp type="datetime">2017-06-25T20:44:41Z</timestamp>
+            </status-history>
+          </status-history>
+          <evidence type="array"/>
+          <transaction>
+            <id>%s</id>
+            <amount>100.00</amount>
+            <created-at>2017-06-21T20:44:41Z</created-at>
+            <order-id nil="true"/>
+            <purchase-order-number nil="true"/>
+            <payment-instrument-subtype>Visa</payment-instrument-subtype>
+          </transaction>
+          <date-opened type=\"date\">2014-03-28</date-opened>
+        </dispute>
+        """ % (id, id)
+
     def __subscription_sample_xml(self, id):
         return """
             <subscription>
@@ -585,40 +882,6 @@ class WebhookTestingGateway(object):
             </account-updater-daily-report>
             """
 
-    # NEXT_MAJOR_VERSION Remove this class as legacy Ideal has been removed/disabled in the Braintree Gateway
-    # DEPRECATED If you're looking to accept iDEAL as a payment method contact accounts@braintreepayments.com for a solution.
-    def __ideal_payment_complete_sample_xml(self, id):
-        return """
-            <ideal-payment>
-                <id>%s</id>
-                <status>COMPLETE</status>
-                <issuer>ABCISSUER</issuer>
-                <order-id>ORDERABC</order-id>
-                <currency>EUR</currency>
-                <amount>10.00</amount>
-                <created-at>2016-11-29T23:27:34.547Z</created-at>
-                <approval-url>https://example.com</approval-url>
-                <ideal-transaction-id>1234567890</ideal-transaction-id>
-            </ideal-payment>
-            """ % id
-
-    # NEXT_MAJOR_VERSION Remove this class as legacy Ideal has been removed/disabled in the Braintree Gateway
-    # DEPRECATED If you're looking to accept iDEAL as a payment method contact accounts@braintreepayments.com for a solution.
-    def __ideal_payment_failed_sample_xml(self, id):
-        return """
-            <ideal-payment>
-                <id>%s</id>
-                <status>FAILED</status>
-                <issuer>ABCISSUER</issuer>
-                <order-id>ORDERABC</order-id>
-                <currency>EUR</currency>
-                <amount>10.00</amount>
-                <created-at>2016-11-29T23:27:34.547Z</created-at>
-                <approval-url>https://example.com</approval-url>
-                <ideal-transaction-id>1234567890</ideal-transaction-id>
-            </ideal-payment>
-            """ % id
-
     def __granted_payment_instrument_update(self):
         return """
             <granted-payment-instrument-update>
@@ -637,6 +900,9 @@ class WebhookTestingGateway(object):
             </granted-payment-instrument-update>
             """
 
+    def __granted_payment_method_revoked(self, id):
+        return self.__venmo_account_xml(id)
+
     def __payment_method_revoked_by_customer(self, id):
         return """
             <paypal-account>
@@ -672,3 +938,70 @@ class WebhookTestingGateway(object):
                 </transaction>
             </local-payment>
             """
+
+    def __local_payment_expired(self):
+        return """
+            <local-payment-expired>
+                <payment-id>a-payment-id</payment-id>
+                <payment-context-id>a-context-payment-id</payment-context-id>
+            </local-payment-expired>
+            """
+
+    def __local_payment_funded(self):
+        return """
+            <local-payment-funded>
+                <payment-id>a-payment-id</payment-id>
+                <payment-context-id>a-context-payment-id</payment-context-id>
+                <transaction>
+                    <id>1</id>
+                    <status>settled</status>
+                    <amount>10.00</amount>
+                    <order-id>order1234</order-id>
+                </transaction>
+            </local-payment-funded>
+            """
+
+    def __local_payment_reversed(self):
+        return """
+            <local-payment-reversed>
+                <payment-id>a-payment-id</payment-id>
+            </local-payment-reversed>
+            """
+
+    def __payment_method_customer_data_updated_sample_xml(self, id):
+        return """
+            <payment-method-customer-data-updated-metadata>
+                <token>TOKEN-12345</token>
+                <payment-method>%s</payment-method>
+                <datetime-updated type='dateTime'>2022-01-01T21:28:37Z</datetime-updated>
+                <enriched-customer-data>
+                    <fields-updated type='array'>
+                        <item>username</item>
+                    </fields-updated>
+                    <profile-data>
+                        <username>venmo_username</username>
+                        <first-name>John</first-name>
+                        <last-name>Doe</last-name>
+                        <phone-number>1231231234</phone-number>
+                        <email>john.doe@paypal.com</email>
+                    </profile-data>
+                </enriched-customer-data>
+            </payment-method-customer-data-updated-metadata>
+            """ % self.__venmo_account_xml(id)
+
+    def __venmo_account_xml(self, id):
+        return """
+            <venmo-account>
+                <created-at type="datetime">2018-10-11T21:28:37Z</created-at>
+                <updated-at type="datetime">2018-10-11T21:28:37Z</updated-at>
+                <default type="boolean">true</default>
+                <image-url>https://assets.braintreegateway.com/payment_method_logo/venmo.png?environment=test</image-url>
+                <token>%s</token>
+                <source-description>Venmo Account: venmojoe</source-description>
+                <username>venmojoe</username>
+                <venmo-user-id>456</venmo-user-id>
+                <subscriptions type="array"/>
+                <customer-id>venmo_customer_id</customer-id>
+                <global-id>cGF5bWVudG1ldGhvZF92ZW5tb2FjY291bnQ</global-id>
+            </venmo-account>
+            """ % id
diff --git a/ci.sh b/ci.sh
deleted file mode 100755
index efe6fb5..0000000
--- a/ci.sh
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/bin/bash
-
-if [[ "$1" == "http" ]]; then
-  python_tests="test:http"
-fi
-
-if [[ "$1" == "python3" ]]; then
-  /usr/local/lib/python3.3/bin/nosetests-3.3
-else
-  env rake $python_tests --trace
-fi
diff --git a/debian/changelog b/debian/changelog
index 21e4f45..7204d01 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,10 @@
+python-braintree (4.18.0-1) UNRELEASED; urgency=low
+
+  * New upstream release.
+  * New upstream release.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Fri, 20 Jan 2023 15:49:42 -0000
+
 python-braintree (3.57.1-1) unstable; urgency=medium
 
   * New upstream release.
diff --git a/docs/Makefile b/docs/Makefile
deleted file mode 100644
index 9df406c..0000000
--- a/docs/Makefile
+++ /dev/null
@@ -1,89 +0,0 @@
-# Makefile for Sphinx documentation
-#
-
-# You can set these variables from the command line.
-SPHINXOPTS    =
-SPHINXBUILD   = sphinx-build
-PAPER         =
-BUILDDIR      = _build
-
-# Internal variables.
-PAPEROPT_a4     = -D latex_paper_size=a4
-PAPEROPT_letter = -D latex_paper_size=letter
-ALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
-
-.PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest
-
-help:
-	@echo "Please use \`make <target>' where <target> is one of"
-	@echo "  html      to make standalone HTML files"
-	@echo "  dirhtml   to make HTML files named index.html in directories"
-	@echo "  pickle    to make pickle files"
-	@echo "  json      to make JSON files"
-	@echo "  htmlhelp  to make HTML files and a HTML help project"
-	@echo "  qthelp    to make HTML files and a qthelp project"
-	@echo "  latex     to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
-	@echo "  changes   to make an overview of all changed/added/deprecated items"
-	@echo "  linkcheck to check all external links for integrity"
-	@echo "  doctest   to run all doctests embedded in the documentation (if enabled)"
-
-clean:
-	-rm -rf $(BUILDDIR)/*
-
-html:
-	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
-	@echo
-	@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
-
-dirhtml:
-	$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
-	@echo
-	@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
-
-pickle:
-	$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
-	@echo
-	@echo "Build finished; now you can process the pickle files."
-
-json:
-	$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
-	@echo
-	@echo "Build finished; now you can process the JSON files."
-
-htmlhelp:
-	$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
-	@echo
-	@echo "Build finished; now you can run HTML Help Workshop with the" \
-	      ".hhp project file in $(BUILDDIR)/htmlhelp."
-
-qthelp:
-	$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
-	@echo
-	@echo "Build finished; now you can run "qcollectiongenerator" with the" \
-	      ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
-	@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Braintree.qhcp"
-	@echo "To view the help file:"
-	@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Braintree.qhc"
-
-latex:
-	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
-	@echo
-	@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
-	@echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
-	      "run these through (pdf)latex."
-
-changes:
-	$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
-	@echo
-	@echo "The overview file is in $(BUILDDIR)/changes."
-
-linkcheck:
-	$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
-	@echo
-	@echo "Link check complete; look for any errors in the above output " \
-	      "or in $(BUILDDIR)/linkcheck/output.txt."
-
-doctest:
-	$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
-	@echo "Testing of doctests in the sources finished, look at the " \
-	      "results in $(BUILDDIR)/doctest/output.txt."
diff --git a/docs/_static/.git_empty_dir b/docs/_static/.git_empty_dir
deleted file mode 100644
index e69de29..0000000
diff --git a/docs/address.rst b/docs/address.rst
deleted file mode 100644
index 2b71100..0000000
--- a/docs/address.rst
+++ /dev/null
@@ -1,5 +0,0 @@
-Address
-=====================================
-
-.. automodule:: braintree.address
-   :members:
diff --git a/docs/conf.py b/docs/conf.py
deleted file mode 100644
index 4eb9134..0000000
--- a/docs/conf.py
+++ /dev/null
@@ -1,197 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Braintree documentation build configuration file, created by
-# sphinx-quickstart on Mon Mar 29 14:46:55 2010.
-#
-# This file is execfile()d with the current directory set to its containing dir.
-#
-# Note that not all possible configuration values are present in this
-# autogenerated file.
-#
-# All configuration values have a default; values that are commented out
-# serve to show the default.
-
-import sys, os
-
-# If extensions (or modules to document with autodoc) are in another directory,
-# add these directories to sys.path here. If the directory is relative to the
-# documentation root, use os.path.abspath to make it absolute, like shown here.
-#sys.path.append(os.path.abspath('.'))
-sys.path.insert(0, os.path.dirname(__file__) + '/../')
-import braintree
-
-# -- General configuration -----------------------------------------------------
-
-# Add any Sphinx extension module names here, as strings. They can be extensions
-# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
-extensions = ['sphinx.ext.autodoc']
-
-# Add any paths that contain templates here, relative to this directory.
-#templates_path = ['_templates']
-templates_path = []
-
-# The suffix of source filenames.
-source_suffix = '.rst'
-
-# The encoding of source files.
-#source_encoding = 'utf-8'
-
-# The master toctree document.
-master_doc = 'index'
-
-# General information about the project.
-project = u'Braintree'
-copyright = u'2012, Braintree'
-
-# The version info for the project you're documenting, acts as replacement for
-# |version| and |release|, also used in various other places throughout the
-# built documents.
-#
-# The short X.Y version.
-version = braintree.version.Version
-# The full version, including alpha/beta/rc tags.
-release = braintree.version.Version
-
-# The language for content autogenerated by Sphinx. Refer to documentation
-# for a list of supported languages.
-#language = None
-
-# There are two options for replacing |today|: either, you set today to some
-# non-false value, then it is used:
-#today = ''
-# Else, today_fmt is used as the format for a strftime call.
-#today_fmt = '%B %d, %Y'
-
-# List of documents that shouldn't be included in the build.
-#unused_docs = []
-
-# List of directories, relative to source directory, that shouldn't be searched
-# for source files.
-exclude_trees = ['_build']
-
-# The reST default role (used for this markup: `text`) to use for all documents.
-#default_role = None
-
-# If true, '()' will be appended to :func: etc. cross-reference text.
-#add_function_parentheses = True
-
-# If true, the current module name will be prepended to all description
-# unit titles (such as .. function::).
-#add_module_names = True
-
-# If true, sectionauthor and moduleauthor directives will be shown in the
-# output. They are ignored by default.
-#show_authors = False
-
-# The name of the Pygments (syntax highlighting) style to use.
-pygments_style = 'sphinx'
-
-# A list of ignored prefixes for module index sorting.
-#modindex_common_prefix = []
-
-
-# -- Options for HTML output ---------------------------------------------------
-
-# The theme to use for HTML and HTML Help pages.  Major themes that come with
-# Sphinx are currently 'default' and 'sphinxdoc'.
-html_theme = 'default'
-
-# Theme options are theme-specific and customize the look and feel of a theme
-# further.  For a list of options available for each theme, see the
-# documentation.
-#html_theme_options = {}
-
-# Add any paths that contain custom themes here, relative to this directory.
-#html_theme_path = []
-
-# The name for this set of Sphinx documents.  If None, it defaults to
-# "<project> v<release> documentation".
-#html_title = None
-
-# A shorter title for the navigation bar.  Default is the same as html_title.
-#html_short_title = None
-
-# The name of an image file (relative to this directory) to place at the top
-# of the sidebar.
-#html_logo = None
-
-# The name of an image file (within the static path) to use as favicon of the
-# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
-# pixels large.
-#html_favicon = None
-
-# Add any paths that contain custom static files (such as style sheets) here,
-# relative to this directory. They are copied after the builtin static files,
-# so a file named "default.css" will overwrite the builtin "default.css".
-html_static_path = ['_static']
-
-# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
-# using the given strftime format.
-#html_last_updated_fmt = '%b %d, %Y'
-
-# If true, SmartyPants will be used to convert quotes and dashes to
-# typographically correct entities.
-#html_use_smartypants = True
-
-# Custom sidebar templates, maps document names to template names.
-#html_sidebars = {}
-
-# Additional templates that should be rendered to pages, maps page names to
-# template names.
-#html_additional_pages = {}
-
-# If false, no module index is generated.
-#html_use_modindex = True
-
-# If false, no index is generated.
-#html_use_index = True
-
-# If true, the index is split into individual pages for each letter.
-#html_split_index = False
-
-# If true, links to the reST sources are added to the pages.
-#html_show_sourcelink = True
-
-# If true, an OpenSearch description file will be output, and all pages will
-# contain a <link> tag referring to it.  The value of this option must be the
-# base URL from which the finished HTML is served.
-#html_use_opensearch = ''
-
-# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
-#html_file_suffix = ''
-
-# Output file base name for HTML help builder.
-htmlhelp_basename = 'Braintreedoc'
-
-
-# -- Options for LaTeX output --------------------------------------------------
-
-# The paper size ('letter' or 'a4').
-#latex_paper_size = 'letter'
-
-# The font size ('10pt', '11pt' or '12pt').
-#latex_font_size = '10pt'
-
-# Grouping the document tree into LaTeX files. List of tuples
-# (source start file, target name, title, author, documentclass [howto/manual]).
-latex_documents = [
-  ('index', 'Braintree.tex', u'Braintree Documentation',
-   u'Braintree', 'manual'),
-]
-
-# The name of an image file (relative to this directory) to place at the top of
-# the title page.
-#latex_logo = None
-
-# For "manual" documents, if this is true, then toplevel headings are parts,
-# not chapters.
-#latex_use_parts = False
-
-# Additional stuff for the LaTeX preamble.
-#latex_preamble = ''
-
-# Documents to append as an appendix to all manuals.
-#latex_appendices = []
-
-# If false, no module index is generated.
-#latex_use_modindex = True
diff --git a/docs/configuration.rst b/docs/configuration.rst
deleted file mode 100644
index ea43f45..0000000
--- a/docs/configuration.rst
+++ /dev/null
@@ -1,5 +0,0 @@
-Configuration
-=====================================
-
-.. automodule:: braintree.configuration
-   :members:
diff --git a/docs/credit_card.rst b/docs/credit_card.rst
deleted file mode 100644
index 8f40ddf..0000000
--- a/docs/credit_card.rst
+++ /dev/null
@@ -1,5 +0,0 @@
-Credit Card
-=====================================
-
-.. automodule:: braintree.credit_card
-   :members:
diff --git a/docs/credit_card_verification.rst b/docs/credit_card_verification.rst
deleted file mode 100644
index 2036335..0000000
--- a/docs/credit_card_verification.rst
+++ /dev/null
@@ -1,5 +0,0 @@
-Credit Card Verification
-=====================================
-
-.. automodule:: braintree.credit_card_verification
-   :members:
diff --git a/docs/customer.rst b/docs/customer.rst
deleted file mode 100644
index daae0b8..0000000
--- a/docs/customer.rst
+++ /dev/null
@@ -1,5 +0,0 @@
-Customer
-=====================================
-
-.. automodule:: braintree.customer
-   :members:
diff --git a/docs/environment.rst b/docs/environment.rst
deleted file mode 100644
index 9e34b47..0000000
--- a/docs/environment.rst
+++ /dev/null
@@ -1,5 +0,0 @@
-Environment
-=====================================
-
-.. automodule:: braintree.environment
-   :members:
diff --git a/docs/error_codes.rst b/docs/error_codes.rst
deleted file mode 100644
index f813889..0000000
--- a/docs/error_codes.rst
+++ /dev/null
@@ -1,5 +0,0 @@
-Error Codes
-=====================================
-
-.. automodule:: braintree.error_codes
-   :members:
diff --git a/docs/error_result.rst b/docs/error_result.rst
deleted file mode 100644
index 335ee35..0000000
--- a/docs/error_result.rst
+++ /dev/null
@@ -1,5 +0,0 @@
-Error Result
-=====================================
-
-.. automodule:: braintree.error_result
-   :members:
diff --git a/docs/exceptions/authentication_error.rst b/docs/exceptions/authentication_error.rst
deleted file mode 100644
index 16a74d0..0000000
--- a/docs/exceptions/authentication_error.rst
+++ /dev/null
@@ -1,5 +0,0 @@
-Authentication Error
-=====================================
-
-.. automodule:: braintree.exceptions.authentication_error
-   :members:
diff --git a/docs/exceptions/authorization_error.rst b/docs/exceptions/authorization_error.rst
deleted file mode 100644
index b34525e..0000000
--- a/docs/exceptions/authorization_error.rst
+++ /dev/null
@@ -1,5 +0,0 @@
-Authorization Error
-=====================================
-
-.. automodule:: braintree.exceptions.authorization_error
-   :members:
diff --git a/docs/exceptions/down_for_maintenance_error.rst b/docs/exceptions/down_for_maintenance_error.rst
deleted file mode 100644
index c73eb23..0000000
--- a/docs/exceptions/down_for_maintenance_error.rst
+++ /dev/null
@@ -1,5 +0,0 @@
-Down For Maintenance Error
-=====================================
-
-.. automodule:: braintree.exceptions.down_for_maintenance_error
-   :members:
diff --git a/docs/exceptions/forged_query_string_error.rst b/docs/exceptions/forged_query_string_error.rst
deleted file mode 100644
index 4d223c7..0000000
--- a/docs/exceptions/forged_query_string_error.rst
+++ /dev/null
@@ -1,5 +0,0 @@
-Forged Query String Error
-=====================================
-
-.. automodule:: braintree.exceptions.forged_query_string_error
-   :members:
diff --git a/docs/exceptions/not_found_error.rst b/docs/exceptions/not_found_error.rst
deleted file mode 100644
index 7f21f9b..0000000
--- a/docs/exceptions/not_found_error.rst
+++ /dev/null
@@ -1,5 +0,0 @@
-Not Found Error
-=====================================
-
-.. automodule:: braintree.exceptions.not_found_error
-   :members:
diff --git a/docs/exceptions/server_error.rst b/docs/exceptions/server_error.rst
deleted file mode 100644
index 93b2845..0000000
--- a/docs/exceptions/server_error.rst
+++ /dev/null
@@ -1,5 +0,0 @@
-Server Error
-=====================================
-
-.. automodule:: braintree.exceptions.server_error
-   :members:
diff --git a/docs/exceptions/unexpected_error.rst b/docs/exceptions/unexpected_error.rst
deleted file mode 100644
index 99a3013..0000000
--- a/docs/exceptions/unexpected_error.rst
+++ /dev/null
@@ -1,5 +0,0 @@
-Unexpected Error
-=====================================
-
-.. automodule:: braintree.exceptions.unexpected_error
-   :members:
diff --git a/docs/exceptions/upgrade_required_error.rst b/docs/exceptions/upgrade_required_error.rst
deleted file mode 100644
index c815c99..0000000
--- a/docs/exceptions/upgrade_required_error.rst
+++ /dev/null
@@ -1,5 +0,0 @@
-Upgrade Required Error
-=====================================
-
-.. automodule:: braintree.exceptions.upgrade_required_error
-   :members:
diff --git a/docs/index.rst b/docs/index.rst
deleted file mode 100644
index d2f25e6..0000000
--- a/docs/index.rst
+++ /dev/null
@@ -1,79 +0,0 @@
-.. Braintree documentation master file, created by
-   sphinx-quickstart on Mon Mar 29 14:46:55 2010.
-   You can adapt this file completely to your liking, but it should at least
-   contain the root `toctree` directive.
-
-Braintree Python Client Library
-=====================================
-
-The Braintree library provides integration access to the Braintree Gateway.
-
-Quick Start
------------
-
-See: https://developers.braintreepayments.com/start/hello-server/python
-
-Braintree Objects
------------------
-
-.. toctree::
-   :maxdepth: 2
-
-   address
-   credit_card
-   credit_card_verification
-   customer
-   subscription
-   transaction
-
-Utility Objects
----------------
-
-.. toctree::
-   :maxdepth: 2
-
-   resource_collection
-   transparent_redirect
-   successful_result
-
-Errors
-------
-
-.. toctree::
-   :maxdepth: 2
-
-   error_codes
-   error_result
-   validation_error
-   validation_error_collection
-
-Configuration
--------------
-
-.. toctree::
-   :maxdepth: 2
-
-   configuration
-   environment
-
-Exceptions
-----------
-
-.. toctree::
-   :maxdepth: 2
-
-   exceptions/authentication_error
-   exceptions/authorization_error
-   exceptions/down_for_maintenance_error
-   exceptions/forged_query_string_error
-   exceptions/not_found_error
-   exceptions/server_error
-   exceptions/unexpected_error
-   exceptions/upgrade_required_error
-
-Indices
--------
-
-* :ref:`genindex`
-* :ref:`modindex`
-* :ref:`search`
diff --git a/docs/resource_collection.rst b/docs/resource_collection.rst
deleted file mode 100644
index adf927b..0000000
--- a/docs/resource_collection.rst
+++ /dev/null
@@ -1,5 +0,0 @@
-Resource Collection
-=====================================
-
-.. automodule:: braintree.resource_collection
-   :members:
diff --git a/docs/subscription.rst b/docs/subscription.rst
deleted file mode 100644
index a4a9998..0000000
--- a/docs/subscription.rst
+++ /dev/null
@@ -1,5 +0,0 @@
-Subscription
-=====================================
-
-.. automodule:: braintree.subscription
-   :members:
diff --git a/docs/successful_result.rst b/docs/successful_result.rst
deleted file mode 100644
index 96caf4b..0000000
--- a/docs/successful_result.rst
+++ /dev/null
@@ -1,5 +0,0 @@
-Successful Result
-=====================================
-
-.. automodule:: braintree.successful_result
-   :members:
diff --git a/docs/transaction.rst b/docs/transaction.rst
deleted file mode 100644
index 5c9a0ea..0000000
--- a/docs/transaction.rst
+++ /dev/null
@@ -1,5 +0,0 @@
-Transaction
-=====================================
-
-.. automodule:: braintree.transaction
-   :members:
diff --git a/docs/transparent_redirect.rst b/docs/transparent_redirect.rst
deleted file mode 100644
index 8131362..0000000
--- a/docs/transparent_redirect.rst
+++ /dev/null
@@ -1,5 +0,0 @@
-Transparent Redirect
-=====================================
-
-.. automodule:: braintree.transparent_redirect
-   :members:
diff --git a/docs/validation_error.rst b/docs/validation_error.rst
deleted file mode 100644
index 5326079..0000000
--- a/docs/validation_error.rst
+++ /dev/null
@@ -1,5 +0,0 @@
-Validation Error
-=====================================
-
-.. automodule:: braintree.validation_error
-   :members:
diff --git a/docs/validation_error_collection.rst b/docs/validation_error_collection.rst
deleted file mode 100644
index 04cbbbe..0000000
--- a/docs/validation_error_collection.rst
+++ /dev/null
@@ -1,5 +0,0 @@
-Validation Error Collection
-=====================================
-
-.. automodule:: braintree.validation_error_collection
-   :members:
diff --git a/requirements.txt b/requirements.txt
deleted file mode 100644
index 8910361..0000000
--- a/requirements.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-requests>=0.11.1,<3.0
-mock>=2.0,<3.0
-nose>=1.3.7,<2.0
-twine>=1.9,<2.0
diff --git a/setup.cfg b/setup.cfg
index 2a9acf1..adf5ed7 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,2 +1,7 @@
 [bdist_wheel]
 universal = 1
+
+[egg_info]
+tag_build = 
+tag_date = 0
+
diff --git a/setup.py b/setup.py
index e9179d1..c4a4284 100644
--- a/setup.py
+++ b/setup.py
@@ -7,17 +7,17 @@ long_description = """
         The Braintree Python SDK provides integration access to the Braintree Gateway.
 
         1. https://github.com/braintree/braintree_python - README and Samples
-        2. https://developers.braintreepayments.com/python/sdk/server/overview - API Reference
+        2. https://developer.paypal.com/braintree/docs/reference/overview - API Reference
       """
 
 setup(
     name="braintree",
-    version="3.57.1",
+    version="4.18.0",
     description="Braintree Python Library",
     long_description=long_description,
     author="Braintree",
     author_email="support@braintreepayments.com",
-    url="https://developers.braintreepayments.com/python/sdk/server/overview",
+    url="https://developer.paypal.com/braintree/docs/reference/overview",
     packages=["braintree", "braintree.dispute_details", "braintree.exceptions", "braintree.exceptions.http", "braintree.merchant_account", "braintree.util", "braintree.test"],
     package_data={"braintree": ["ssl/*"]},
     install_requires=["requests>=0.11.1,<3.0"],
@@ -25,10 +25,9 @@ setup(
     license="MIT",
     classifiers=[
         "License :: OSI Approved :: MIT License",
-        "Programming Language :: Python :: 2.6",
-        "Programming Language :: Python :: 2.7",
-        "Programming Language :: Python :: 3.3",
-        "Programming Language :: Python :: 3.4",
-        "Programming Language :: Python :: 3.5"
+        "Programming Language :: Python :: 3.5",
+        "Programming Language :: Python :: 3.6",
+        "Programming Language :: Python :: 3.7",
+        "Programming Language :: Python :: 3.8"
     ]
 )
diff --git a/tests/__init__.py b/tests/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/tests/fixtures/bt_logo.png b/tests/fixtures/bt_logo.png
deleted file mode 100644
index 943ec2c..0000000
Binary files a/tests/fixtures/bt_logo.png and /dev/null differ
diff --git a/tests/fixtures/gif_extension_bt_logo.gif b/tests/fixtures/gif_extension_bt_logo.gif
deleted file mode 100644
index 943ec2c..0000000
Binary files a/tests/fixtures/gif_extension_bt_logo.gif and /dev/null differ
diff --git a/tests/fixtures/malformed_pdf.pdf b/tests/fixtures/malformed_pdf.pdf
deleted file mode 100644
index 7fc3a29..0000000
--- a/tests/fixtures/malformed_pdf.pdf
+++ /dev/null
@@ -1 +0,0 @@
-XXXXXXXXXXX¾?^gfkbfu¶šÙú”¼Åxc¯ÄÖñ•:x„BºS·É=]óqçÅÒÐwî+=	x	wÁ¼/7ú7ÀݛeÀÅA¿8?À­‹6Á2[î(†¤Ö@+3[ß}
diff --git a/tests/fixtures/too_long.pdf b/tests/fixtures/too_long.pdf
deleted file mode 100644
index 24039f4..0000000
Binary files a/tests/fixtures/too_long.pdf and /dev/null differ
diff --git a/tests/integration/__init__.py b/tests/integration/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/tests/integration/test_add_ons.py b/tests/integration/test_add_ons.py
deleted file mode 100644
index 6f6c422..0000000
--- a/tests/integration/test_add_ons.py
+++ /dev/null
@@ -1,34 +0,0 @@
-from tests.test_helper import *
-
-class TestAddOn(unittest.TestCase):
-    def test_all_returns_all_add_ons(self):
-        new_id = str(random.randint(1, 1000000))
-        attributes = {
-            "amount": "100.00",
-            "description": "some description",
-            "id": new_id,
-            "kind": "add_on",
-            "name": "python_add_on",
-            "never_expires": False,
-            "number_of_billing_cycles": 1
-        }
-
-        Configuration.instantiate().http().post(Configuration.instantiate().base_merchant_path() + "/modifications/create_modification_for_tests", {"modification": attributes})
-
-        add_ons = AddOn.all()
-
-        for add_on in add_ons:
-            if add_on.id == new_id:
-                break
-        else:
-            add_on = None
-
-        self.assertNotEqual(None, add_on)
-
-        self.assertEqual(Decimal("100.00"), add_on.amount)
-        self.assertEqual("some description", add_on.description)
-        self.assertEqual(new_id, add_on.id)
-        self.assertEqual("add_on", add_on.kind)
-        self.assertEqual("python_add_on", add_on.name)
-        self.assertEqual(False, add_on.never_expires)
-        self.assertEqual(add_on.number_of_billing_cycles, 1)
diff --git a/tests/integration/test_address.py b/tests/integration/test_address.py
deleted file mode 100644
index 1862cd8..0000000
--- a/tests/integration/test_address.py
+++ /dev/null
@@ -1,167 +0,0 @@
-from tests.test_helper import *
-
-class TestAddress(unittest.TestCase):
-    def test_create_returns_successful_result_if_valid(self):
-        customer = Customer.create().customer
-        result = Address.create({
-            "customer_id": customer.id,
-            "first_name": "Ben",
-            "last_name": "Moore",
-            "company": "Moore Co.",
-            "street_address": "1811 E Main St",
-            "extended_address": "Suite 200",
-            "locality": "Chicago",
-            "region": "Illinois",
-            "postal_code": "60622",
-            "country_name": "United States of America",
-            "country_code_alpha2": "US",
-            "country_code_alpha3": "USA",
-            "country_code_numeric": "840"
-        })
-
-        self.assertTrue(result.is_success)
-        address = result.address
-        self.assertEqual(customer.id, address.customer_id)
-        self.assertEqual("Ben", address.first_name)
-        self.assertEqual("Moore", address.last_name)
-        self.assertEqual("Moore Co.", address.company)
-        self.assertEqual("1811 E Main St", address.street_address)
-        self.assertEqual("Suite 200", address.extended_address)
-        self.assertEqual("Chicago", address.locality)
-        self.assertEqual("Illinois", address.region)
-        self.assertEqual("60622", address.postal_code)
-        self.assertEqual("US", address.country_code_alpha2)
-        self.assertEqual("USA", address.country_code_alpha3)
-        self.assertEqual("840", address.country_code_numeric)
-        self.assertEqual("United States of America", address.country_name)
-
-    def test_error_response_if_invalid(self):
-        customer = Customer.create().customer
-        result = Address.create({
-            "customer_id": customer.id,
-            "country_name": "zzzzzz",
-            "country_code_alpha2": "zz",
-            "country_code_alpha3": "zzz",
-            "country_code_numeric": "000"
-        })
-
-        self.assertFalse(result.is_success)
-
-        country_name_errors = result.errors.for_object("address").on("country_name")
-        self.assertEqual(1, len(country_name_errors))
-        self.assertEqual(ErrorCodes.Address.CountryNameIsNotAccepted, country_name_errors[0].code)
-
-        country_code_alpha2_errors = result.errors.for_object("address").on("country_code_alpha2")
-        self.assertEqual(1, len(country_code_alpha2_errors))
-        self.assertEqual(ErrorCodes.Address.CountryCodeAlpha2IsNotAccepted, country_code_alpha2_errors[0].code)
-
-        country_code_alpha3_errors = result.errors.for_object("address").on("country_code_alpha3")
-        self.assertEqual(1, len(country_code_alpha3_errors))
-        self.assertEqual(ErrorCodes.Address.CountryCodeAlpha3IsNotAccepted, country_code_alpha3_errors[0].code)
-
-        country_code_numeric_errors = result.errors.for_object("address").on("country_code_numeric")
-        self.assertEqual(1, len(country_code_numeric_errors))
-        self.assertEqual(ErrorCodes.Address.CountryCodeNumericIsNotAccepted, country_code_numeric_errors[0].code)
-
-    def test_error_response_if_inconsistent_country(self):
-        customer = Customer.create().customer
-        result = Address.create({
-            "customer_id": customer.id,
-            "country_code_alpha2": "US",
-            "country_code_alpha3": "MEX"
-        })
-
-        self.assertFalse(result.is_success)
-
-        address_errors = result.errors.for_object("address").on("base")
-        self.assertEqual(1, len(address_errors))
-        self.assertEqual(ErrorCodes.Address.InconsistentCountry, address_errors[0].code)
-
-    def test_delete_with_valid_customer_id_and_address_id(self):
-        customer = Customer.create().customer
-        address = Address.create({"customer_id": customer.id, "street_address": "123 Main St."}).address
-        result = Address.delete(customer.id, address.id)
-
-        self.assertTrue(result.is_success)
-
-    @raises(NotFoundError)
-    def test_delete_with_valid_customer_id_and_non_existing_address(self):
-        customer = Customer.create().customer
-        Address.delete(customer.id, "notreal")
-
-    def test_find_with_valid_customer_id_and_address_id(self):
-        customer = Customer.create().customer
-        address = Address.create({"customer_id": customer.id, "street_address": "123 Main St."}).address
-        found_address = Address.find(customer.id, address.id)
-
-        self.assertEqual(address.street_address, found_address.street_address)
-
-    @raises_with_regexp(NotFoundError,
-        "address for customer 'notreal' with id 'badaddress' not found")
-    def test_find_with_invalid_customer_id_and_address_id(self):
-        Address.find("notreal", "badaddress")
-
-    def test_update_with_valid_values(self):
-        customer = Customer.create().customer
-        address = Address.create({
-            "customer_id": customer.id,
-            "street_address": "1811 E Main St",
-            "extended_address": "Suite 200",
-            "locality": "Chicago",
-            "region": "Illinois",
-            "postal_code": "60622",
-            "country_name": "United States of America"
-        }).address
-
-        result = Address.update(customer.id, address.id, {
-            "street_address": "123 E New St",
-            "extended_address": "New Suite 3",
-            "locality": "Chicago",
-            "region": "Illinois",
-            "postal_code": "60621",
-            "country_code_alpha2": "MX",
-            "country_code_alpha3": "MEX",
-            "country_code_numeric": "484",
-            "country_name": "Mexico"
-        })
-
-        self.assertTrue(result.is_success)
-        address = result.address
-        self.assertEqual(customer.id, address.customer_id)
-        self.assertEqual("123 E New St", address.street_address)
-        self.assertEqual("New Suite 3", address.extended_address)
-        self.assertEqual("Chicago", address.locality)
-        self.assertEqual("Illinois", address.region)
-        self.assertEqual("60621", address.postal_code)
-        self.assertEqual("MX", address.country_code_alpha2)
-        self.assertEqual("MEX", address.country_code_alpha3)
-        self.assertEqual("484", address.country_code_numeric)
-        self.assertEqual("Mexico", address.country_name)
-
-    def test_update_with_invalid_values(self):
-        customer = Customer.create().customer
-        address = Address.create({
-            "customer_id": customer.id,
-            "street_address": "1811 E Main St",
-            "extended_address": "Suite 200",
-            "locality": "Chicago",
-            "region": "Illinois",
-            "postal_code": "60622",
-            "country_name": "United States of America"
-        }).address
-
-        result = Address.update(customer.id, address.id, {
-            "street_address": "123 E New St",
-            "country_name": "United States of Invalid"
-        })
-
-        self.assertFalse(result.is_success)
-
-        country_name_errors = result.errors.for_object("address").on("country_name")
-        self.assertEqual(1, len(country_name_errors))
-        self.assertEqual(ErrorCodes.Address.CountryNameIsNotAccepted, country_name_errors[0].code)
-
-    @raises(NotFoundError)
-    def test_update_raises_not_found_error_if_given_bad_address(self):
-        customer = Customer.create().customer
-        Address.update(customer.id, "notfound", {"street_address": "123 Main St."})
diff --git a/tests/integration/test_braintree_gateway.py b/tests/integration/test_braintree_gateway.py
deleted file mode 100644
index 82673b2..0000000
--- a/tests/integration/test_braintree_gateway.py
+++ /dev/null
@@ -1,71 +0,0 @@
-from unittest import TestCase
-
-from braintree.braintree_gateway import BraintreeGateway
-from braintree.configuration import Configuration
-from braintree.environment import Environment
-
-class TestBraintreeGateway(TestCase):
-
-    @staticmethod
-    def get_gateway():
-        config = Configuration("development", "integration_merchant_id",
-                               public_key="integration_public_key",
-                               private_key="integration_private_key")
-        return BraintreeGateway(config)
-
-    def test_can_make_tokenize_credit_card_via_graphql(self):
-        definition = """
-          mutation ExampleServerSideSingleUseToken($input: TokenizeCreditCardInput!) {
-            tokenizeCreditCard(input: $input) {
-              paymentMethod {
-                id
-                usage
-                details {
-                  ... on CreditCardDetails {
-                    bin
-                    brandCode
-                    last4
-                    expirationYear
-                    expirationMonth
-                  }
-                }
-              }
-            }
-          }
-        """
-        variables = {
-            "input" : {
-                "creditCard" : {
-                    "number" : "4005519200000004",
-                    "expirationYear": "2024",
-                    "expirationMonth": "05",
-                    "cardholderName": "Joe Bloggs"
-                }
-            }
-        }
-        gateway = self.get_gateway()
-        response = gateway.graphql_client.query(definition, variables)
-
-        payment_method = response["data"]["tokenizeCreditCard"]["paymentMethod"]
-        details = payment_method["details"]
-
-        self.assertTrue("data" in response)
-        self.assertTrue("id" in payment_method)
-        self.assertEqual(details["bin"], "400551")
-        self.assertEqual(details["last4"], "0004");
-        self.assertEqual(details["brandCode"], "VISA");
-        self.assertEqual(details["expirationMonth"], "05");
-        self.assertEqual(details["expirationYear"], "2024");
-
-    def test_can_make_graphql_queries_without_variables(self):
-        definition = """
-          query {
-            ping
-          }
-        """
-        gateway = self.get_gateway()
-        response = gateway.graphql_client.query(definition)
-
-        self.assertTrue("data" in response)
-        self.assertTrue("ping" in response["data"])
-        self.assertEqual("pong", response["data"]["ping"])
diff --git a/tests/integration/test_client_token.py b/tests/integration/test_client_token.py
deleted file mode 100644
index 9e5f94a..0000000
--- a/tests/integration/test_client_token.py
+++ /dev/null
@@ -1,167 +0,0 @@
-from tests.test_helper import *
-import json
-import urllib
-import datetime
-import braintree
-from braintree.util import Http
-from base64 import b64decode
-
-class TestClientTokenGenerate(unittest.TestCase):
-    def test_allows_client_token_version_to_be_specified(self):
-        client_token = ClientToken.generate({"version": 1})
-        version = json.loads(client_token)["version"]
-        self.assertEqual(1, version)
-
-    def test_error_in_generate_raises_value_error(self):
-        self.assertRaises(ValueError, ClientToken.generate, {
-            "customer_id": "i_am_not_a_real_customer"
-        })
-
-class TestClientToken(unittest.TestCase):
-    def test_is_authorized_with_authorization_fingerprint(self):
-        config = Configuration.instantiate()
-        client_token = TestHelper.generate_decoded_client_token()
-        authorization_fingerprint = json.loads(client_token)["authorizationFingerprint"]
-
-        http = ClientApiHttp(config, {
-            "authorization_fingerprint": authorization_fingerprint,
-            "shared_customer_identifier": "fake_identifier",
-            "shared_customer_identifier_type": "testing"
-        })
-
-        status_code, _ = http.get_cards()
-        self.assertEqual(200, status_code)
-
-    def test_client_token_version_defaults_to_two(self):
-        client_token = TestHelper.generate_decoded_client_token()
-        version = json.loads(client_token)["version"]
-
-        self.assertEqual(2, version)
-
-    def test_can_pass_verify_card(self):
-        config = Configuration.instantiate()
-        result = braintree.Customer.create()
-        customer_id = result.customer.id
-
-        client_token = TestHelper.generate_decoded_client_token({
-            "customer_id": customer_id,
-            "options": {
-                "verify_card": True
-            }
-        })
-        authorization_fingerprint = json.loads(client_token)["authorizationFingerprint"]
-        http = ClientApiHttp(config, {
-            "authorization_fingerprint": authorization_fingerprint,
-            "shared_customer_identifier": "fake_identifier",
-            "shared_customer_identifier_type": "testing"
-        })
-
-        status_code, _ = http.add_card({
-            "credit_card": {
-                "number": "4000111111111115",
-                "expiration_month": "11",
-                "expiration_year": "2099",
-            }
-        })
-        self.assertEqual(422, status_code)
-
-    def test_can_pass_make_default(self):
-        config = Configuration.instantiate()
-        result = braintree.Customer.create()
-        customer_id = result.customer.id
-
-        client_token = TestHelper.generate_decoded_client_token({
-            "customer_id": customer_id,
-            "options": {
-                "make_default": True
-            }
-        })
-        authorization_fingerprint = json.loads(client_token)["authorizationFingerprint"]
-        http = ClientApiHttp(config, {
-            "authorization_fingerprint": authorization_fingerprint,
-            "shared_customer_identifier": "fake_identifier",
-            "shared_customer_identifier_type": "testing"
-        })
-
-        status_code, _ = http.add_card({
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_month": "11",
-                "expiration_year": "2099",
-            }
-        })
-        self.assertEqual(201, status_code)
-
-        status_code, _ = http.add_card({
-            "credit_card": {
-                "number": "4005519200000004",
-                "expiration_month": "11",
-                "expiration_year": "2099",
-            }
-        })
-        self.assertEqual(201, status_code)
-
-        customer = braintree.Customer.find(customer_id)
-        self.assertEqual(2, len(customer.credit_cards))
-        for credit_card in customer.credit_cards:
-            if credit_card.bin == "400551":
-                self.assertTrue(credit_card.default)
-
-    def test_can_pass_fail_on_duplicate_payment_method(self):
-        config = Configuration.instantiate()
-        result = braintree.Customer.create()
-        customer_id = result.customer.id
-
-        client_token = TestHelper.generate_decoded_client_token({
-            "customer_id": customer_id,
-        })
-        authorization_fingerprint = json.loads(client_token)["authorizationFingerprint"]
-        http = ClientApiHttp(config, {
-            "authorization_fingerprint": authorization_fingerprint,
-            "shared_customer_identifier": "fake_identifier",
-            "shared_customer_identifier_type": "testing"
-        })
-
-        status_code, _ = http.add_card({
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_month": "11",
-                "expiration_year": "2099",
-            }
-        })
-        self.assertEqual(201, status_code)
-
-        client_token = TestHelper.generate_decoded_client_token({
-            "customer_id": customer_id,
-            "options": {
-                "fail_on_duplicate_payment_method": True
-            }
-        })
-        authorization_fingerprint = json.loads(client_token)["authorizationFingerprint"]
-        http.set_authorization_fingerprint(authorization_fingerprint)
-        status_code, _ = http.add_card({
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_month": "11",
-                "expiration_year": "2099",
-            }
-        })
-        self.assertEqual(422, status_code)
-
-        customer = braintree.Customer.find(customer_id)
-        self.assertEqual(1, len(customer.credit_cards))
-
-    def test_can_pass_merchant_account_id(self):
-        expected_merchant_account_id = TestHelper.non_default_merchant_account_id
-        client_token = TestHelper.generate_decoded_client_token({
-            "merchant_account_id": expected_merchant_account_id
-        })
-        merchant_account_id = json.loads(client_token)["merchantAccountId"]
-
-        self.assertEqual(expected_merchant_account_id, merchant_account_id)
-
-    @raises_with_regexp(Exception, "'Invalid keys: merchant_id'")
-    def test_required_data_cannot_be_overridden(self):
-        TestHelper.generate_decoded_client_token({
-            "merchant_id": "1234"
-        })
diff --git a/tests/integration/test_coinbase.py b/tests/integration/test_coinbase.py
deleted file mode 100644
index 7d214a3..0000000
--- a/tests/integration/test_coinbase.py
+++ /dev/null
@@ -1,29 +0,0 @@
-from tests.test_helper import *
-
-from braintree.test.nonces import Nonces
-from braintree.exceptions.not_found_error import NotFoundError
-from braintree.error_codes import ErrorCodes
-
-class TestCoinbase(unittest.TestCase):
-
-    def test_customer(self):
-        result = Customer.create({"payment_method_nonce": Nonces.Coinbase})
-
-        self.assertFalse(result.is_success)
-        self.assertEquals(ErrorCodes.PaymentMethod.PaymentMethodNoLongerSupported, result.errors.for_object("coinbase_account").on("base")[0].code)
-
-    def test_vault(self):
-        result = Customer.create()
-        result = PaymentMethod.create({
-            "customer_id": result.customer.id,
-            "payment_method_nonce": Nonces.Coinbase
-        })
-
-        self.assertFalse(result.is_success)
-        self.assertEquals(ErrorCodes.PaymentMethod.PaymentMethodNoLongerSupported, result.errors.for_object("coinbase_account").on("base")[0].code)
-
-    def test_transaction(self):
-        result = Transaction.sale({"payment_method_nonce": Nonces.Coinbase, "amount": "1.00"})
-
-        self.assertFalse(result.is_success)
-        self.assertEquals(ErrorCodes.PaymentMethod.PaymentMethodNoLongerSupported, result.errors.for_object("transaction").on("base")[0].code)
diff --git a/tests/integration/test_credentials_parser.py b/tests/integration/test_credentials_parser.py
deleted file mode 100644
index bab7f02..0000000
--- a/tests/integration/test_credentials_parser.py
+++ /dev/null
@@ -1,80 +0,0 @@
-from tests.test_helper import *
-from braintree.test.nonces import Nonces
-from braintree.credentials_parser import CredentialsParser
-
-class TestCredentialsParser(unittest.TestCase):
-    def test_parses_client_credentials(self):
-        parser = CredentialsParser(
-            client_id="client_id$development$integration_client_id",
-            client_secret="client_secret$development$integration_client_secret"
-        )
-        parser.parse_client_credentials()
-
-        self.assertEqual("client_id$development$integration_client_id", parser.client_id)
-        self.assertEqual("client_secret$development$integration_client_secret", parser.client_secret)
-        self.assertEqual(braintree.Environment.Development, parser.environment)
-
-    def test_error_on_inconsistent_environment(self):
-        with self.assertRaises(ConfigurationError) as error:
-            parser = CredentialsParser(
-                client_id="client_id$qa$integration_client_id",
-                client_secret="client_secret$development$integration_client_secret"
-            )
-            parser.parse_client_credentials()
-
-        config_error = error.exception
-        self.assertIn("Mismatched credential environments", str(config_error))
-
-    def test_error_on_missing_client_id(self):
-        with self.assertRaises(ConfigurationError) as error:
-            parser = CredentialsParser(
-                client_id=None,
-                client_secret="client_secret$development$integration_client_secret"
-            )
-            parser.parse_client_credentials()
-
-        config_error = error.exception
-        self.assertIn("Missing client_id", str(config_error))
-
-    def test_error_on_missing_client_secret(self):
-        with self.assertRaises(ConfigurationError) as error:
-            parser = CredentialsParser(
-                client_id="client_id$development$integration_client_id",
-                client_secret=None
-            )
-            parser.parse_client_credentials()
-
-        config_error = error.exception
-        self.assertIn("Missing client_secret", str(config_error))
-
-    def test_error_on_invalid_client_id(self):
-        with self.assertRaises(ConfigurationError) as error:
-            parser = CredentialsParser(
-                client_id="client_secret$development$integration_client_id",
-                client_secret="client_secret$development$integration_client_secret"
-            )
-            parser.parse_client_credentials()
-
-        config_error = error.exception
-        self.assertIn("Value passed for client_id is not a client_id", str(config_error))
-
-    def test_error_on_invalid_client_secret(self):
-        with self.assertRaises(ConfigurationError) as error:
-            parser = CredentialsParser(
-                client_id="client_id$development$integration_client_id",
-                client_secret="client_id$development$integration_client_secret"
-            )
-            parser.parse_client_credentials()
-
-        config_error = error.exception
-        self.assertIn("Value passed for client_secret is not a client_secret", str(config_error))
-
-    def test_parses_access_token(self):
-        parser = CredentialsParser(
-            access_token="access_token$development$integration_merchant_id$fb27c79dd"
-        )
-        parser.parse_access_token()
-
-        self.assertEqual("access_token$development$integration_merchant_id$fb27c79dd", parser.access_token)
-        self.assertEqual("integration_merchant_id", parser.merchant_id)
-        self.assertEqual(braintree.Environment.Development, parser.environment)
diff --git a/tests/integration/test_credit_card.py b/tests/integration/test_credit_card.py
deleted file mode 100644
index d0613ac..0000000
--- a/tests/integration/test_credit_card.py
+++ /dev/null
@@ -1,1392 +0,0 @@
-from tests.test_helper import *
-from braintree.test.credit_card_defaults import CreditCardDefaults
-from braintree.test.credit_card_numbers import CreditCardNumbers
-import braintree.test.venmo_sdk as venmo_sdk
-
-class TestCreditCard(unittest.TestCase):
-    def test_create_with_three_d_secure_nonce(self):
-        customer_id = Customer.create().customer.id
-        result = CreditCard.create({
-            "customer_id": customer_id,
-            "payment_method_nonce": Nonces.ThreeDSecureVisaFullAuthentication,
-            "options": {
-                "verify_card": "true",
-            }
-        })
-
-        self.assertTrue(result.is_success)
-
-        three_d_secure_info = result.credit_card.verification.three_d_secure_info
-
-        self.assertEqual("Y", three_d_secure_info.enrolled)
-        self.assertEqual("authenticate_successful", three_d_secure_info.status)
-        self.assertEqual(True, three_d_secure_info.liability_shifted)
-        self.assertEqual(True, three_d_secure_info.liability_shift_possible)
-        self.assertEqual("cavv_value", three_d_secure_info.cavv)
-        self.assertEqual("xid_value", three_d_secure_info.xid)
-        self.assertEqual(None, three_d_secure_info.ds_transaction_id)
-        self.assertEqual("05", three_d_secure_info.eci_flag)
-        self.assertEqual("1.0.2", three_d_secure_info.three_d_secure_version)
-
-    def test_create_adds_credit_card_to_existing_customer(self):
-        customer = Customer.create().customer
-        result = CreditCard.create({
-            "customer_id": customer.id,
-            "number": "4111111111111111",
-            "expiration_date": "05/2014",
-            "cvv": "100",
-            "cardholder_name": "John Doe"
-        })
-
-        self.assertTrue(result.is_success)
-        credit_card = result.credit_card
-        self.assertTrue(re.search(r"\A\w{4,}\Z", credit_card.token) is not None)
-        self.assertEqual("411111", credit_card.bin)
-        self.assertEqual("1111", credit_card.last_4)
-        self.assertEqual("05", credit_card.expiration_month)
-        self.assertEqual("2014", credit_card.expiration_year)
-        self.assertEqual("05/2014", credit_card.expiration_date)
-        self.assertEqual("John Doe", credit_card.cardholder_name)
-        self.assertNotEqual(re.search(r"\A\w{32}\Z", credit_card.unique_number_identifier), None)
-        self.assertFalse(credit_card.venmo_sdk)
-        self.assertNotEqual(re.search("png", credit_card.image_url), None)
-
-    def test_create_and_make_default(self):
-        customer = Customer.create().customer
-        card1 = CreditCard.create({
-            "customer_id": customer.id,
-            "number": "4111111111111111",
-            "expiration_date": "05/2014",
-            "cvv": "100",
-            "cardholder_name": "John Doe"
-        }).credit_card
-
-        self.assertTrue(card1.default)
-
-        card2 = CreditCard.create({
-            "customer_id": customer.id,
-            "number": "4111111111111111",
-            "expiration_date": "05/2014",
-            "cvv": "100",
-            "cardholder_name": "John Doe",
-            "options":
-                {"make_default": True}
-        }).credit_card
-
-        card1 = CreditCard.find(card1.token)
-        self.assertFalse(card1.default)
-        self.assertTrue(card2.default)
-
-    def test_create_with_expiration_month_and_year(self):
-        customer = Customer.create().customer
-        result = CreditCard.create({
-            "customer_id": customer.id,
-            "number": "4111111111111111",
-            "expiration_month": "05",
-            "expiration_year": "2014",
-            "cvv": "100",
-            "cardholder_name": "John Doe"
-        })
-
-        self.assertTrue(result.is_success)
-        credit_card = result.credit_card
-        self.assertEqual("05/2014", credit_card.expiration_date)
-
-    def test_create_with_security_params(self):
-        customer = Customer.create().customer
-        result = CreditCard.create({
-            "customer_id": customer.id,
-            "number": "4111111111111111",
-            "expiration_month": "05",
-            "expiration_year": "2014",
-            "cvv": "100",
-            "cardholder_name": "John Doe",
-            "device_session_id": "abc123",
-            "fraud_merchant_id": "456"
-        })
-
-        self.assertTrue(result.is_success)
-
-    def test_create_can_specify_the_desired_token(self):
-        token = str(random.randint(1, 1000000))
-        customer = Customer.create().customer
-        result = CreditCard.create({
-            "customer_id": customer.id,
-            "number": "4111111111111111",
-            "expiration_date": "05/2014",
-            "token": token
-        })
-
-        self.assertTrue(result.is_success)
-        credit_card = result.credit_card
-        self.assertEqual(token, credit_card.token)
-
-    def test_create_with_billing_address(self):
-        customer = Customer.create().customer
-        result = CreditCard.create({
-            "customer_id": customer.id,
-            "number": "4111111111111111",
-            "expiration_date": "05/2014",
-            "billing_address": {
-                "street_address": "123 Abc Way",
-                "locality": "Chicago",
-                "region": "Illinois",
-                "postal_code": "60622",
-                "country_code_alpha2": "MX",
-                "country_code_alpha3": "MEX",
-                "country_code_numeric": "484",
-                "country_name": "Mexico"
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        address = result.credit_card.billing_address
-        self.assertEqual("123 Abc Way", address.street_address)
-        self.assertEqual("Chicago", address.locality)
-        self.assertEqual("Illinois", address.region)
-        self.assertEqual("60622", address.postal_code)
-        self.assertEqual("MX", address.country_code_alpha2)
-        self.assertEqual("MEX", address.country_code_alpha3)
-        self.assertEqual("484", address.country_code_numeric)
-        self.assertEqual("Mexico", address.country_name)
-
-    def test_create_with_billing_address_id(self):
-        customer = Customer.create().customer
-        address = Address.create({
-            "customer_id": customer.id,
-            "street_address": "123 Abc Way"
-        }).address
-
-        result = CreditCard.create({
-            "customer_id": customer.id,
-            "number": "4111111111111111",
-            "expiration_date": "05/2014",
-            "billing_address_id": address.id
-        })
-
-        self.assertTrue(result.is_success)
-        billing_address = result.credit_card.billing_address
-        self.assertEqual(address.id, billing_address.id)
-        self.assertEqual("123 Abc Way", billing_address.street_address)
-
-    def test_create_without_billing_address_still_has_billing_address_method(self):
-        customer = Customer.create().customer
-        result = CreditCard.create({
-            "customer_id": customer.id,
-            "number": "4111111111111111",
-            "expiration_date": "05/2014",
-        })
-        self.assertTrue(result.is_success)
-        self.assertEqual(None, result.credit_card.billing_address)
-
-
-    def test_unsuccessful_create_with_card_verification_returns_risk_data(self):
-        with AdvancedFraudIntegrationMerchant():
-            customer = Customer.create().customer
-            result = CreditCard.create({
-                "customer_id": customer.id,
-                "number": "4000111111111115",
-                "expiration_date": "05/2014",
-                "options": {"verify_card": True},
-                "device_session_id": "abc123"
-            })
-
-            self.assertFalse(result.is_success)
-            verification = result.credit_card_verification
-            self.assertIsInstance(verification.risk_data, RiskData)
-            self.assertTrue(hasattr(verification.risk_data, 'id'))
-            self.assertEqual("Approve", verification.risk_data.decision)
-            self.assertTrue(hasattr(verification.risk_data, 'device_data_captured'))
-            self.assertTrue(hasattr(verification.risk_data, 'fraud_service_provider'))
-
-    def test_successful_create_with_card_verification_returns_risk_data(self):
-        with AdvancedFraudIntegrationMerchant():
-            customer = Customer.create().customer
-            result = CreditCard.create({
-                "customer_id": customer.id,
-                "number": "4111111111111111",
-                "expiration_date": "05/2014",
-                "options": {"verify_card": True},
-                "device_session_id": "abc123"
-            })
-
-            self.assertTrue(result.is_success)
-            verification = result.credit_card.verification
-            self.assertIsInstance(verification.risk_data, RiskData)
-            self.assertTrue(hasattr(verification.risk_data, 'id'))
-            self.assertEqual("Approve", verification.risk_data.decision)
-            self.assertTrue(hasattr(verification.risk_data, 'device_data_captured'))
-            self.assertTrue(hasattr(verification.risk_data, 'fraud_service_provider'))
-
-    def test_create_with_card_verification(self):
-        customer = Customer.create().customer
-        result = CreditCard.create({
-            "customer_id": customer.id,
-            "number": "4000111111111115",
-            "expiration_date": "05/2014",
-            "options": {"verify_card": True}
-        })
-
-        self.assertFalse(result.is_success)
-        verification = result.credit_card_verification
-        self.assertEqual(CreditCardVerification.Status.ProcessorDeclined, verification.status)
-        self.assertEqual("2000", verification.processor_response_code)
-        self.assertEqual("Do Not Honor", verification.processor_response_text)
-        self.assertEqual("I", verification.cvv_response_code)
-        self.assertEqual(None, verification.avs_error_response_code)
-        self.assertEqual("I", verification.avs_postal_code_response_code)
-        self.assertEqual("I", verification.avs_street_address_response_code)
-        self.assertEqual(Decimal("0.00"), verification.amount)
-        self.assertEqual("USD", verification.currency_iso_code)
-        self.assertEqual(TestHelper.default_merchant_account_id, verification.merchant_account_id)
-
-    def test_create_with_card_verification_with_overridden_amount(self):
-        customer = Customer.create().customer
-        result = CreditCard.create({
-            "customer_id": customer.id,
-            "number": "4000111111111115",
-            "expiration_date": "05/2014",
-            "options": {"verify_card": True, "verification_amount": "1.02"}
-        })
-
-        self.assertFalse(result.is_success)
-        verification = result.credit_card_verification
-        self.assertEqual(CreditCardVerification.Status.ProcessorDeclined, verification.status)
-        self.assertEqual("2000", verification.processor_response_code)
-        self.assertEqual("Do Not Honor", verification.processor_response_text)
-        self.assertEqual("I", verification.cvv_response_code)
-        self.assertEqual(None, verification.avs_error_response_code)
-        self.assertEqual("I", verification.avs_postal_code_response_code)
-        self.assertEqual("I", verification.avs_street_address_response_code)
-        self.assertEqual(Decimal("1.02"), verification.amount)
-        self.assertEqual("USD", verification.currency_iso_code)
-        self.assertEqual(TestHelper.default_merchant_account_id, verification.merchant_account_id)
-
-    def test_create_with_card_verification_and_non_default_merchant_account(self):
-        customer = Customer.create().customer
-        result = CreditCard.create({
-            "customer_id": customer.id,
-            "number": "4000111111111115",
-            "expiration_date": "05/2014",
-            "options": {
-                "verification_merchant_account_id": TestHelper.non_default_merchant_account_id,
-                "verify_card": True
-            }
-        })
-
-        self.assertFalse(result.is_success)
-        verification = result.credit_card_verification
-        self.assertEqual(CreditCardVerification.Status.ProcessorDeclined, verification.status)
-        self.assertEqual(None, verification.gateway_rejection_reason)
-        self.assertEqual(TestHelper.non_default_merchant_account_id, verification.merchant_account_id)
-
-    def test_verify_gateway_rejected_responds_to_processor_response_code(self):
-        old_merchant_id = Configuration.merchant_id
-        old_public_key = Configuration.public_key
-        old_private_key = Configuration.private_key
-
-        try:
-            Configuration.merchant_id = "processing_rules_merchant_id"
-            Configuration.public_key = "processing_rules_public_key"
-            Configuration.private_key = "processing_rules_private_key"
-
-            customer = Customer.create().customer
-            result = CreditCard.create({
-                "customer_id": customer.id,
-                "number": "4111111111111111",
-                "expiration_date": "05/2014",
-                "billing_address": {
-                    "postal_code": "20000"
-                },
-                "options": {
-                    "verify_card": True
-                }
-            })
-
-
-            self.assertFalse(result.is_success)
-            self.assertEqual('1000', result.credit_card_verification.processor_response_code)
-            self.assertEqual('Approved', result.credit_card_verification.processor_response_text)
-        finally:
-            Configuration.merchant_id = old_merchant_id
-            Configuration.public_key = old_public_key
-            Configuration.private_key = old_private_key
-
-    def test_expose_gateway_rejection_reason_on_verification(self):
-        old_merchant_id = Configuration.merchant_id
-        old_public_key = Configuration.public_key
-        old_private_key = Configuration.private_key
-
-        try:
-            Configuration.merchant_id = "processing_rules_merchant_id"
-            Configuration.public_key = "processing_rules_public_key"
-            Configuration.private_key = "processing_rules_private_key"
-
-            customer = Customer.create().customer
-            result = CreditCard.create({
-                "customer_id": customer.id,
-                "number": "4111111111111111",
-                "expiration_date": "05/2014",
-                "cvv": "200",
-                "options": {
-                    "verify_card": True
-                }
-            })
-
-            self.assertFalse(result.is_success)
-            verification = result.credit_card_verification
-            self.assertEqual(Transaction.GatewayRejectionReason.Cvv, verification.gateway_rejection_reason)
-        finally:
-            Configuration.merchant_id = old_merchant_id
-            Configuration.public_key = old_public_key
-            Configuration.private_key = old_private_key
-
-    def test_create_with_card_verification_set_to_false(self):
-        customer = Customer.create().customer
-        result = CreditCard.create({
-            "customer_id": customer.id,
-            "number": "4000111111111115",
-            "expiration_date": "05/2014",
-            "options": {"verify_card": False}
-        })
-
-        self.assertTrue(result.is_success)
-
-    def test_create_with_fail_on_duplicate_payment_method_set_to_true(self):
-        customer = Customer.create().customer
-        CreditCard.create({
-            "customer_id": customer.id,
-            "number": "4000111111111115",
-            "expiration_date": "05/2014"
-        })
-
-        result = CreditCard.create({
-            "customer_id": customer.id,
-            "number": "4000111111111115",
-            "expiration_date": "05/2014",
-            "options": {"fail_on_duplicate_payment_method": True}
-        })
-
-        self.assertFalse(result.is_success)
-        self.assertEqual("Duplicate card exists in the vault.", result.message)
-
-        credit_card_number_errors = result.errors.for_object("credit_card").on("number")
-        self.assertEqual(1, len(credit_card_number_errors))
-        self.assertEqual(ErrorCodes.CreditCard.DuplicateCardExists, credit_card_number_errors[0].code)
-
-    def test_create_with_invalid_invalid_options(self):
-        customer = Customer.create().customer
-        result = CreditCard.create({
-            "customer_id": customer.id,
-            "number": "4111111111111111",
-            "expiration_date": "invalid_date",
-        })
-
-        self.assertFalse(result.is_success)
-        self.assertEqual("Expiration date is invalid.", result.message)
-
-        expiration_date_errors = result.errors.for_object("credit_card").on("expiration_date")
-        self.assertEqual(1, len(expiration_date_errors))
-        self.assertEqual(ErrorCodes.CreditCard.ExpirationDateIsInvalid, expiration_date_errors[0].code)
-
-    def test_create_with_invalid_country_codes(self):
-        customer = Customer.create().customer
-        result = CreditCard.create({
-            "customer_id": customer.id,
-            "number": "4111111111111111",
-            "expiration_date": "05/2012",
-            "billing_address": {
-                "country_code_alpha2": "ZZ",
-                "country_code_alpha3": "ZZZ",
-                "country_code_numeric": "000",
-                "country_name": "zzzzzzz"
-            }
-        })
-
-        self.assertFalse(result.is_success)
-
-        country_code_alpha2_errors = result.errors.for_object("credit_card").for_object("billing_address").on("country_code_alpha2")
-        self.assertEqual(1, len(country_code_alpha2_errors))
-        self.assertEqual(ErrorCodes.Address.CountryCodeAlpha2IsNotAccepted, country_code_alpha2_errors[0].code)
-
-        country_code_alpha3_errors = result.errors.for_object("credit_card").for_object("billing_address").on("country_code_alpha3")
-        self.assertEqual(1, len(country_code_alpha3_errors))
-        self.assertEqual(ErrorCodes.Address.CountryCodeAlpha3IsNotAccepted, country_code_alpha3_errors[0].code)
-
-        country_code_numeric_errors = result.errors.for_object("credit_card").for_object("billing_address").on("country_code_numeric")
-        self.assertEqual(1, len(country_code_numeric_errors))
-        self.assertEqual(ErrorCodes.Address.CountryCodeNumericIsNotAccepted, country_code_numeric_errors[0].code)
-
-        country_name_errors = result.errors.for_object("credit_card").for_object("billing_address").on("country_name")
-        self.assertEqual(1, len(country_name_errors))
-        self.assertEqual(ErrorCodes.Address.CountryNameIsNotAccepted, country_name_errors[0].code)
-
-    def test_create_with_venmo_sdk_payment_method_code(self):
-        customer = Customer.create().customer
-        result = CreditCard.create({
-            "customer_id": customer.id,
-            "venmo_sdk_payment_method_code": venmo_sdk.VisaPaymentMethodCode
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertEqual("411111", result.credit_card.bin)
-        self.assertFalse(result.credit_card.venmo_sdk)
-
-    def test_create_with_invalid_venmo_sdk_payment_method_code(self):
-        customer = Customer.create().customer
-        result = CreditCard.create({
-            "customer_id": customer.id,
-            "venmo_sdk_payment_method_code": venmo_sdk.InvalidPaymentMethodCode
-        })
-
-        self.assertFalse(result.is_success)
-        self.assertEqual("Invalid VenmoSDK payment method code", result.message)
-
-        venmo_sdk_payment_method_code_errors = result.errors.for_object("credit_card").on("venmo_sdk_payment_method_code")
-        self.assertEqual(1, len(venmo_sdk_payment_method_code_errors))
-        self.assertEqual(ErrorCodes.CreditCard.InvalidVenmoSDKPaymentMethodCode, venmo_sdk_payment_method_code_errors[0].code)
-
-    def test_create_with_payment_method_nonce(self):
-        config = Configuration.instantiate()
-        authorization_fingerprint = json.loads(TestHelper.generate_decoded_client_token())["authorizationFingerprint"]
-        http = ClientApiHttp(config, {
-            "authorization_fingerprint": authorization_fingerprint,
-            "shared_customer_identifier": "fake_identifier",
-            "shared_customer_identifier_type": "testing"
-        })
-        _, response = http.add_card({
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_month": "11",
-                "expiration_year": "2099",
-            },
-            "share": True
-        })
-        nonce = json.loads(response)["creditCards"][0]["nonce"]
-        customer = Customer.create().customer
-
-        result = CreditCard.create({
-            "customer_id": customer.id,
-            "payment_method_nonce": nonce
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertEqual("411111", result.credit_card.bin)
-
-    def test_create_with_venmo_sdk_session(self):
-        customer = Customer.create().customer
-        result = CreditCard.create({
-            "customer_id": customer.id,
-            "number": "4111111111111111",
-            "expiration_date": "05/2014",
-            "cvv": "100",
-            "cardholder_name": "John Doe",
-            "options": {
-                "venmo_sdk_session": venmo_sdk.Session
-            }
-        })
-        self.assertTrue(result.is_success)
-        self.assertFalse(result.credit_card.venmo_sdk)
-
-    def test_create_with_invalid_venmo_sdk_session(self):
-        customer = Customer.create().customer
-        result = CreditCard.create({
-            "customer_id": customer.id,
-            "number": "4111111111111111",
-            "expiration_date": "05/2014",
-            "cvv": "100",
-            "cardholder_name": "John Doe",
-            "options": {
-                "venmo_sdk_session": venmo_sdk.InvalidSession
-            }
-        })
-        self.assertTrue(result.is_success)
-        self.assertFalse(result.credit_card.venmo_sdk)
-
-    def test_update_with_valid_options(self):
-        customer = Customer.create().customer
-        credit_card = CreditCard.create({
-            "customer_id": customer.id,
-            "number": "4111111111111111",
-            "expiration_date": "05/2014",
-            "cvv": "100",
-            "cardholder_name": "John Doe"
-        }).credit_card
-
-        result = CreditCard.update(credit_card.token, {
-            "number": "5105105105105100",
-            "expiration_date": "06/2010",
-            "cvv": "123",
-            "cardholder_name": "Jane Jones"
-        })
-
-        self.assertTrue(result.is_success)
-        credit_card = result.credit_card
-        self.assertTrue(re.search(r"\A\w{4,}\Z", credit_card.token) is not None)
-        self.assertEqual("510510", credit_card.bin)
-        self.assertEqual("5100", credit_card.last_4)
-        self.assertEqual("06", credit_card.expiration_month)
-        self.assertEqual("2010", credit_card.expiration_year)
-        self.assertEqual("06/2010", credit_card.expiration_date)
-        self.assertEqual("Jane Jones", credit_card.cardholder_name)
-
-    def test_update_billing_address_creates_new_by_default(self):
-        customer = Customer.create().customer
-        initial_credit_card = CreditCard.create({
-            "customer_id": customer.id,
-            "number": "4111111111111111",
-            "expiration_date": "05/2014",
-            "billing_address": {
-                "street_address": "123 Nigeria Ave",
-            }
-        }).credit_card
-
-        updated_credit_card = CreditCard.update(initial_credit_card.token, {
-            "billing_address": {
-                "region": "IL",
-                "country_code_alpha2": "NG",
-                "country_code_alpha3": "NGA",
-                "country_code_numeric": "566",
-                "country_name": "Nigeria"
-            }
-        }).credit_card
-
-        self.assertEqual("IL", updated_credit_card.billing_address.region)
-        self.assertEqual("NG", updated_credit_card.billing_address.country_code_alpha2)
-        self.assertEqual("NGA", updated_credit_card.billing_address.country_code_alpha3)
-        self.assertEqual("566", updated_credit_card.billing_address.country_code_numeric)
-        self.assertEqual("Nigeria", updated_credit_card.billing_address.country_name)
-        self.assertEqual(None, updated_credit_card.billing_address.street_address)
-        self.assertNotEqual(initial_credit_card.billing_address.id, updated_credit_card.billing_address.id)
-
-    def test_update_billing_address_when_update_existing_is_True(self):
-        customer = Customer.create().customer
-        initial_credit_card = CreditCard.create({
-            "customer_id": customer.id,
-            "number": "4111111111111111",
-            "expiration_date": "05/2014",
-            "billing_address": {
-                "street_address": "123 Nigeria Ave",
-            }
-        }).credit_card
-
-        updated_credit_card = CreditCard.update(initial_credit_card.token, {
-            "billing_address": {
-                "region": "IL",
-                "options": {
-                    "update_existing": True
-                }
-            }
-        }).credit_card
-
-        self.assertEqual("IL", updated_credit_card.billing_address.region)
-        self.assertEqual("123 Nigeria Ave", updated_credit_card.billing_address.street_address)
-        self.assertEqual(initial_credit_card.billing_address.id, updated_credit_card.billing_address.id)
-
-    def test_update_and_make_default(self):
-        customer = Customer.create().customer
-        card1 = CreditCard.create({
-            "customer_id": customer.id,
-            "number": "4111111111111111",
-            "expiration_date": "05/2014",
-            "cvv": "100",
-            "cardholder_name": "John Doe"
-        }).credit_card
-        card2 = CreditCard.create({
-            "customer_id": customer.id,
-            "number": "4111111111111111",
-            "expiration_date": "05/2014",
-            "cvv": "100",
-            "cardholder_name": "John Doe"
-        }).credit_card
-
-        self.assertTrue(card1.default)
-        self.assertFalse(card2.default)
-
-        CreditCard.update(card2.token, {
-            "options": {
-                "make_default": True
-            }
-        })
-        self.assertFalse(CreditCard.find(card1.token).default)
-        self.assertTrue(CreditCard.find(card2.token).default)
-
-
-    def test_update_verifies_card_if_option_is_provided(self):
-        customer = Customer.create().customer
-        credit_card = CreditCard.create({
-            "customer_id": customer.id,
-            "number": "4111111111111111",
-            "expiration_date": "05/2014",
-            "cvv": "100",
-            "cardholder_name": "John Doe"
-        }).credit_card
-
-        result = CreditCard.update(credit_card.token, {
-            "number": "4000111111111115",
-            "expiration_date": "06/2010",
-            "cvv": "123",
-            "cardholder_name": "Jane Jones",
-            "options": {"verify_card": True}
-        })
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(CreditCardVerification.Status.ProcessorDeclined, result.credit_card_verification.status)
-
-    def test_update_verifies_card_with_non_default_merchant_account(self):
-        customer = Customer.create().customer
-        credit_card = CreditCard.create({
-            "customer_id": customer.id,
-            "number": "4111111111111111",
-            "expiration_date": "05/2014",
-            "cvv": "100",
-            "cardholder_name": "John Doe"
-        }).credit_card
-
-        result = CreditCard.update(credit_card.token, {
-            "number": "4000111111111115",
-            "expiration_date": "06/2010",
-            "cvv": "123",
-            "cardholder_name": "Jane Jones",
-            "options": {
-                "verification_merchant_account_id": TestHelper.non_default_merchant_account_id,
-                "verify_card": True
-            }
-        })
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(CreditCardVerification.Status.ProcessorDeclined, result.credit_card_verification.status)
-
-    def test_update_billing_address(self):
-        customer = Customer.create().customer
-        credit_card = CreditCard.create({
-            "customer_id": customer.id,
-            "number": "4111111111111111",
-            "expiration_date": "05/2014",
-            "billing_address": {
-                "street_address": "321 Xyz Way",
-                "locality": "Chicago",
-                "region": "Illinois",
-                "postal_code": "60621"
-            }
-        }).credit_card
-
-        result = CreditCard.update(credit_card.token, {
-            "billing_address": {
-                "street_address": "123 Abc Way",
-                "locality": "Chicago",
-                "region": "Illinois",
-                "postal_code": "60622"
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        address = result.credit_card.billing_address
-        self.assertEqual("123 Abc Way", address.street_address)
-        self.assertEqual("Chicago", address.locality)
-        self.assertEqual("Illinois", address.region)
-        self.assertEqual("60622", address.postal_code)
-
-    def test_update_returns_error_if_invalid(self):
-        customer = Customer.create().customer
-        credit_card = CreditCard.create({
-            "customer_id": customer.id,
-            "number": "4111111111111111",
-            "expiration_date": "05/2014"
-        }).credit_card
-
-        result = CreditCard.update(credit_card.token, {
-            "expiration_date": "invalid_date"
-        })
-
-        self.assertFalse(result.is_success)
-
-        expiration_date_errors = result.errors.for_object("credit_card").on("expiration_date")
-        self.assertEqual(1, len(expiration_date_errors))
-        self.assertEqual(ErrorCodes.CreditCard.ExpirationDateIsInvalid, expiration_date_errors[0].code)
-
-    def test_update_returns_error_with_duplicate_payment_method_if_fail_on_duplicate_payment_method_is_set(self):
-        create_result = Customer.create({
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2021",
-            }
-        })
-        self.assertTrue(create_result.is_success)
-
-        update_result = Customer.update(create_result.customer.id, {
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2021",
-                "options": {
-                    "fail_on_duplicate_payment_method": True,
-                },
-            }
-        })
-
-        self.assertFalse(update_result.is_success)
-        number_errors = update_result.errors.for_object("customer").for_object("credit_card").on("number")
-        self.assertEqual(1, len(number_errors))
-        self.assertEqual(ErrorCodes.CreditCard.DuplicateCardExists, number_errors[0].code)
-
-    def test_delete_with_valid_token(self):
-        customer = Customer.create().customer
-        credit_card = CreditCard.create({
-            "customer_id": customer.id,
-            "number": "4111111111111111",
-            "expiration_date": "05/2014"
-        }).credit_card
-
-        result = CreditCard.delete(credit_card.token)
-        self.assertTrue(result.is_success)
-
-    @raises(NotFoundError)
-    def test_delete_raises_error_when_deleting_twice(self):
-        customer = Customer.create().customer
-        credit_card = CreditCard.create({
-            "customer_id": customer.id,
-            "number": "4111111111111111",
-            "expiration_date": "05/2014"
-        }).credit_card
-
-        CreditCard.delete(credit_card.token)
-        CreditCard.delete(credit_card.token)
-
-    @raises(NotFoundError)
-    def test_delete_with_invalid_token(self):
-        CreditCard.delete("notreal")
-
-    def test_find_with_valid_token(self):
-        customer = Customer.create().customer
-        credit_card = CreditCard.create({
-            "customer_id": customer.id,
-            "number": "4111111111111111",
-            "expiration_date": "05/2014"
-        }).credit_card
-
-        found_credit_card = CreditCard.find(credit_card.token)
-        self.assertTrue(re.search(r"\A\w{4,}\Z", found_credit_card.token) is not None)
-        self.assertEqual("411111", found_credit_card.bin)
-        self.assertEqual("1111", found_credit_card.last_4)
-        self.assertEqual("05", found_credit_card.expiration_month)
-        self.assertEqual("2014", found_credit_card.expiration_year)
-        self.assertEqual("05/2014", found_credit_card.expiration_date)
-
-    def test_find_returns_associated_subsriptions(self):
-        customer = Customer.create().customer
-        credit_card = CreditCard.create({
-            "customer_id": customer.id,
-            "number": "4111111111111111",
-            "expiration_date": "05/2014"
-        }).credit_card
-        subscription_id = "id_" + str(random.randint(1, 1000000))
-        subscription = Subscription.create({
-            "id": subscription_id,
-            "plan_id": "integration_trialless_plan",
-            "payment_method_token": credit_card.token,
-            "price": Decimal("1.00")
-        }).subscription
-
-        found_credit_card = CreditCard.find(credit_card.token)
-        subscriptions = found_credit_card.subscriptions
-
-        self.assertEqual(1, len(subscriptions))
-        subscription = subscriptions[0]
-
-        self.assertEqual(subscription_id, subscription.id)
-        self.assertEqual(Decimal("1.00"), subscription.price)
-        self.assertEqual(credit_card.token, subscription.payment_method_token)
-
-    @raises_with_regexp(NotFoundError, "payment method with token 'bad_token' not found")
-    def test_find_with_invalid_token(self):
-        CreditCard.find("bad_token")
-
-    def test_from_nonce_with_unlocked_nonce(self):
-        config = Configuration.instantiate()
-        customer = Customer.create().customer
-
-        client_token = TestHelper.generate_decoded_client_token({
-            "customer_id": customer.id,
-        })
-        authorization_fingerprint = json.loads(client_token)["authorizationFingerprint"]
-        http = ClientApiHttp(config, {
-            "authorization_fingerprint": authorization_fingerprint,
-            "shared_customer_identifier": "fake_identifier",
-            "shared_customer_identifier_type": "testing"
-        })
-
-        status_code, response = http.add_card({
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_month": "11",
-                "expiration_year": "2099",
-            }
-        })
-        self.assertEqual(201, status_code)
-        nonce = json.loads(response)["creditCards"][0]["nonce"]
-
-        card = CreditCard.from_nonce(nonce)
-        customer = Customer.find(customer.id)
-        self.assertEqual(1, len(customer.credit_cards))
-        self.assertEqual(customer.credit_cards[0].token, card.token)
-
-    @raises_with_regexp(NotFoundError, "payment method with nonce .* or not found")
-    def test_from_nonce_with_unlocked_nonce_pointing_to_shared_card(self):
-        config = Configuration.instantiate()
-
-        client_token = TestHelper.generate_decoded_client_token()
-        authorization_fingerprint = json.loads(client_token)["authorizationFingerprint"]
-        http = ClientApiHttp(config, {
-            "authorization_fingerprint": authorization_fingerprint,
-            "shared_customer_identifier": "fake_identifier",
-            "shared_customer_identifier_type": "testing"
-        })
-
-        status_code, response = http.add_card({
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_month": "11",
-                "expiration_year": "2099",
-            },
-            "share": True
-        })
-        self.assertEqual(201, status_code)
-        nonce = json.loads(response)["creditCards"][0]["nonce"]
-
-        CreditCard.from_nonce(nonce)
-
-    @raises_with_regexp(NotFoundError, ".* consumed .*")
-    def test_from_nonce_with_consumed_nonce(self):
-        config = Configuration.instantiate()
-        customer = Customer.create().customer
-
-        client_token = TestHelper.generate_decoded_client_token({
-            "customer_id": customer.id,
-        })
-        authorization_fingerprint = json.loads(client_token)["authorizationFingerprint"]
-        http = ClientApiHttp(config, {
-            "authorization_fingerprint": authorization_fingerprint,
-            "shared_customer_identifier": "fake_identifier",
-            "shared_customer_identifier_type": "testing"
-        })
-
-        status_code, response = http.add_card({
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_month": "11",
-                "expiration_year": "2099",
-            }
-        })
-        self.assertEqual(201, status_code)
-        nonce = json.loads(response)["creditCards"][0]["nonce"]
-
-        CreditCard.from_nonce(nonce)
-        CreditCard.from_nonce(nonce)
-
-    def test_create_from_transparent_redirect(self):
-        customer = Customer.create().customer
-        tr_data = {
-            "credit_card": {
-                "customer_id": customer.id
-            }
-        }
-        post_params = {
-            "tr_data": CreditCard.tr_data_for_create(tr_data, "http://example.com/path?foo=bar"),
-            "credit_card[cardholder_name]": "Card Holder",
-            "credit_card[number]": "4111111111111111",
-            "credit_card[expiration_date]": "05/2012",
-            "credit_card[billing_address][country_code_alpha2]": "MX",
-            "credit_card[billing_address][country_code_alpha3]": "MEX",
-            "credit_card[billing_address][country_code_numeric]": "484",
-            "credit_card[billing_address][country_name]": "Mexico",
-        }
-
-        query_string = TestHelper.simulate_tr_form_post(post_params, CreditCard.transparent_redirect_create_url())
-        result = CreditCard.confirm_transparent_redirect(query_string)
-        self.assertTrue(result.is_success)
-        credit_card = result.credit_card
-        self.assertEqual("411111", credit_card.bin)
-        self.assertEqual("1111", credit_card.last_4)
-        self.assertEqual("05", credit_card.expiration_month)
-        self.assertEqual("2012", credit_card.expiration_year)
-        self.assertEqual(customer.id, credit_card.customer_id)
-        self.assertEqual("MX", credit_card.billing_address.country_code_alpha2)
-        self.assertEqual("MEX", credit_card.billing_address.country_code_alpha3)
-        self.assertEqual("484", credit_card.billing_address.country_code_numeric)
-        self.assertEqual("Mexico", credit_card.billing_address.country_name)
-
-
-    def test_create_from_transparent_redirect_and_make_default(self):
-        customer = Customer.create().customer
-        card1 = CreditCard.create({
-            "customer_id": customer.id,
-            "number": "4111111111111111",
-            "expiration_date": "05/2014",
-            "cvv": "100",
-            "cardholder_name": "John Doe"
-        }).credit_card
-        self.assertTrue(card1.default)
-
-        tr_data = {
-            "credit_card": {
-                "customer_id": customer.id,
-                "options": {
-                    "make_default": True
-                }
-            }
-        }
-        post_params = {
-            "tr_data": CreditCard.tr_data_for_create(tr_data, "http://example.com/path?foo=bar"),
-            "credit_card[cardholder_name]": "Card Holder",
-            "credit_card[number]": "4111111111111111",
-            "credit_card[expiration_date]": "05/2012",
-        }
-
-        query_string = TestHelper.simulate_tr_form_post(post_params, CreditCard.transparent_redirect_create_url())
-        card2 = CreditCard.confirm_transparent_redirect(query_string).credit_card
-
-        self.assertFalse(CreditCard.find(card1.token).default)
-        self.assertTrue(card2.default)
-
-    def test_create_from_transparent_redirect_with_error_result(self):
-        customer = Customer.create().customer
-        tr_data = {
-            "credit_card": {
-                "customer_id": customer.id
-            }
-        }
-
-        post_params = {
-            "tr_data": CreditCard.tr_data_for_create(tr_data, "http://example.com/path"),
-            "credit_card[cardholder_name]": "Card Holder",
-            "credit_card[number]": "eleventy",
-            "credit_card[expiration_date]": "y2k"
-        }
-
-        query_string = TestHelper.simulate_tr_form_post(post_params, CreditCard.transparent_redirect_create_url())
-        result = CreditCard.confirm_transparent_redirect(query_string)
-        self.assertFalse(result.is_success)
-
-        credit_card_number_errors = result.errors.for_object("credit_card").on("number")
-        self.assertEqual(1, len(credit_card_number_errors))
-        self.assertEqual(ErrorCodes.CreditCard.NumberHasInvalidLength, credit_card_number_errors[0].code)
-
-        expiration_date_errors = result.errors.for_object("credit_card").on("expiration_date")
-        self.assertEqual(1, len(expiration_date_errors))
-        self.assertEqual(ErrorCodes.CreditCard.ExpirationDateIsInvalid, expiration_date_errors[0].code)
-
-    def test_update_from_transparent_redirect_with_successful_result(self):
-        old_token = str(random.randint(1, 1000000))
-        new_token = str(random.randint(1, 1000000))
-        credit_card = Customer.create({
-            "credit_card": {
-                "cardholder_name": "Old Cardholder Name",
-                "number": "4111111111111111",
-                "expiration_date": "05/2012",
-                "token": old_token
-            }
-        }).customer.credit_cards[0]
-
-        tr_data = {
-            "payment_method_token": old_token,
-            "credit_card": {
-                "token": new_token
-            }
-        }
-
-        post_params = {
-            "tr_data": CreditCard.tr_data_for_update(tr_data, "http://example.com/path"),
-            "credit_card[cardholder_name]": "New Cardholder Name",
-            "credit_card[expiration_date]": "05/2014"
-        }
-
-        query_string = TestHelper.simulate_tr_form_post(post_params, CreditCard.transparent_redirect_update_url())
-        result = CreditCard.confirm_transparent_redirect(query_string)
-        self.assertTrue(result.is_success)
-        credit_card = result.credit_card
-        self.assertEqual(new_token, credit_card.token)
-        self.assertEqual("411111", credit_card.bin)
-        self.assertEqual("1111", credit_card.last_4)
-        self.assertEqual("05", credit_card.expiration_month)
-        self.assertEqual("2014", credit_card.expiration_year)
-
-    def test_update_from_transparent_redirect_and_make_default(self):
-        customer = Customer.create({
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2012"
-            }
-        }).customer
-        card1 = customer.credit_cards[0]
-
-        card2 = CreditCard.create({
-            "customer_id": customer.id,
-            "number": "4111111111111111",
-            "expiration_date": "05/2014",
-        }).credit_card
-
-        self.assertTrue(card1.default)
-        self.assertFalse(card2.default)
-
-        tr_data = {
-            "payment_method_token": card2.token,
-            "credit_card": {
-                "options": {
-                    "make_default": True
-                }
-            }
-        }
-
-        post_params = {
-            "tr_data": CreditCard.tr_data_for_update(tr_data, "http://example.com/path"),
-            "credit_card[cardholder_name]": "New Cardholder Name",
-            "credit_card[expiration_date]": "05/2014"
-        }
-
-        query_string = TestHelper.simulate_tr_form_post(post_params, CreditCard.transparent_redirect_update_url())
-        CreditCard.confirm_transparent_redirect(query_string)
-
-        self.assertFalse(CreditCard.find(card1.token).default)
-        self.assertTrue(CreditCard.find(card2.token).default)
-
-    def test_update_from_transparent_redirect_and_update_existing_billing_address(self):
-        customer = Customer.create({
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2012",
-                "billing_address": {
-                    "street_address": "123 Old St",
-                    "locality": "Chicago",
-                    "region": "Illinois",
-                    "postal_code": "60621"
-                }
-            }
-        }).customer
-        card = customer.credit_cards[0]
-
-        tr_data = {
-            "payment_method_token": card.token,
-            "credit_card": {
-                "billing_address": {
-                    "street_address": "123 New St",
-                    "locality": "Columbus",
-                    "region": "Ohio",
-                    "postal_code": "43215",
-                    "options": {
-                        "update_existing": True
-                    }
-                }
-            }
-        }
-
-        post_params = {
-            "tr_data": CreditCard.tr_data_for_update(tr_data, "http://example.com/path")
-        }
-
-        query_string = TestHelper.simulate_tr_form_post(post_params, CreditCard.transparent_redirect_update_url())
-
-        CreditCard.confirm_transparent_redirect(query_string)
-
-        self.assertEqual(1, len(Customer.find(customer.id).addresses))
-        updated_card = CreditCard.find(card.token)
-        self.assertEqual("123 New St", updated_card.billing_address.street_address)
-        self.assertEqual("Columbus", updated_card.billing_address.locality)
-        self.assertEqual("Ohio", updated_card.billing_address.region)
-        self.assertEqual("43215", updated_card.billing_address.postal_code)
-
-    def test_update_from_transparent_redirect_with_error_result(self):
-        old_token = str(random.randint(1, 1000000))
-        Customer.create({
-            "credit_card": {
-                "cardholder_name": "Old Cardholder Name",
-                "number": "4111111111111111",
-                "expiration_date": "05/2012",
-                "token": old_token
-            }
-        })
-
-        tr_data = {
-            "payment_method_token": old_token,
-            "credit_card": {
-                "token": "bad token"
-            }
-        }
-
-        post_params = {
-            "tr_data": CreditCard.tr_data_for_update(tr_data, "http://example.com/path"),
-            "credit_card[cardholder_name]": "New Cardholder Name",
-            "credit_card[expiration_date]": "05/2014"
-        }
-
-        query_string = TestHelper.simulate_tr_form_post(post_params, CreditCard.transparent_redirect_update_url())
-        result = CreditCard.confirm_transparent_redirect(query_string)
-        self.assertFalse(result.is_success)
-
-        credit_card_token_errors = result.errors.for_object("credit_card").on("token")
-        self.assertEqual(1, len(credit_card_token_errors))
-        self.assertEqual(ErrorCodes.CreditCard.TokenInvalid, credit_card_token_errors[0].code)
-
-    def test_expired_can_iterate_over_all_items(self):
-        customer_id = Customer.all().first.id
-
-        for _ in range(110 - CreditCard.expired().maximum_size):
-            CreditCard.create({
-                "customer_id": customer_id,
-                "number": "4111111111111111",
-                "expiration_date": "01/2015"
-            })
-
-        collection = CreditCard.expired()
-        self.assertTrue(collection.maximum_size > 100)
-
-        credit_card_tokens = [credit_card.token for credit_card in collection.items]
-        self.assertEqual(collection.maximum_size, len(TestHelper.unique(credit_card_tokens)))
-
-        self.assertEqual(set([True]), TestHelper.unique([credit_card.is_expired for credit_card in collection.items]))
-
-    def test_expiring_between(self):
-        customer_id = Customer.all().first.id
-
-        for _ in range(110 - CreditCard.expiring_between(date(2010, 1, 1), date(2010, 12, 31)).maximum_size):
-            CreditCard.create({
-                "customer_id": customer_id,
-                "number": "4111111111111111",
-                "expiration_date": "05/2010",
-                "cvv": "100",
-                "cardholder_name": "John Doe"
-            })
-
-        collection = CreditCard.expiring_between(date(2010, 1, 1), date(2010, 12, 31))
-        self.assertTrue(collection.maximum_size > 100)
-
-        credit_card_tokens = [credit_card.token for credit_card in collection.items]
-        self.assertEqual(collection.maximum_size, len(TestHelper.unique(credit_card_tokens)))
-
-        self.assertEqual(set(['2010']), TestHelper.unique([credit_card.expiration_year for credit_card in collection.items]))
-
-    def test_commercial_card(self):
-        customer = Customer.create().customer
-        result = CreditCard.create({
-            "customer_id": customer.id,
-            "number": CreditCardNumbers.CardTypeIndicators.Commercial,
-            "expiration_date": "05/2014",
-            "options": {"verify_card": True}
-        })
-
-        credit_card = result.credit_card
-
-        self.assertEqual(CreditCard.Commercial.Yes, credit_card.commercial)
-
-    def test_issuing_bank(self):
-        customer = Customer.create().customer
-        result = CreditCard.create({
-            "customer_id": customer.id,
-            "number": CreditCardNumbers.CardTypeIndicators.IssuingBank,
-            "expiration_date": "05/2014"
-        })
-
-        credit_card = result.credit_card
-
-        self.assertEqual(CreditCardDefaults.IssuingBank, credit_card.issuing_bank)
-
-    def test_country_of_issuance(self):
-        customer = Customer.create().customer
-        result = CreditCard.create({
-            "customer_id": customer.id,
-            "number": CreditCardNumbers.CardTypeIndicators.CountryOfIssuance,
-            "expiration_date": "05/2014",
-            "options": {"verify_card": True}
-        })
-
-        credit_card = result.credit_card
-
-        self.assertEqual(CreditCardDefaults.CountryOfIssuance, credit_card.country_of_issuance)
-
-    def test_durbin_regulated_card(self):
-        customer = Customer.create().customer
-        result = CreditCard.create({
-            "customer_id": customer.id,
-            "number": CreditCardNumbers.CardTypeIndicators.DurbinRegulated,
-            "expiration_date": "05/2014",
-            "options": {"verify_card": True}
-        })
-
-        credit_card = result.credit_card
-
-        self.assertEqual(CreditCard.DurbinRegulated.Yes, credit_card.durbin_regulated)
-
-    def test_debit_card(self):
-        customer = Customer.create().customer
-        result = CreditCard.create({
-            "customer_id": customer.id,
-            "number": CreditCardNumbers.CardTypeIndicators.Debit,
-            "expiration_date": "05/2014",
-            "options": {"verify_card": True}
-        })
-
-        credit_card = result.credit_card
-
-        self.assertEqual(CreditCard.Debit.Yes, credit_card.debit)
-
-    def test_healthcare_card(self):
-        customer = Customer.create().customer
-        result = CreditCard.create({
-            "customer_id": customer.id,
-            "number": CreditCardNumbers.CardTypeIndicators.Healthcare,
-            "expiration_date": "05/2014",
-            "options": {"verify_card": True}
-        })
-
-        credit_card = result.credit_card
-
-        self.assertEqual(CreditCard.Healthcare.Yes, credit_card.healthcare)
-        self.assertEqual("J3", credit_card.product_id)
-
-    def test_payroll_card(self):
-        customer = Customer.create().customer
-        result = CreditCard.create({
-            "customer_id": customer.id,
-            "number": CreditCardNumbers.CardTypeIndicators.Payroll,
-            "expiration_date": "05/2014",
-            "options": {"verify_card": True}
-        })
-
-        credit_card = result.credit_card
-
-        self.assertEqual(CreditCard.Payroll.Yes, credit_card.payroll)
-        self.assertEqual("MSA", credit_card.product_id)
-
-    def test_prepaid_card(self):
-        customer = Customer.create().customer
-        result = CreditCard.create({
-            "customer_id": customer.id,
-            "number": CreditCardNumbers.CardTypeIndicators.Prepaid,
-            "expiration_date": "05/2014",
-            "options": {"verify_card": True}
-        })
-
-        credit_card = result.credit_card
-
-        self.assertEqual(CreditCard.Prepaid.Yes, credit_card.prepaid)
-
-    def test_all_negative_card_type_indicators(self):
-        customer = Customer.create().customer
-        result = CreditCard.create({
-            "customer_id": customer.id,
-            "number": CreditCardNumbers.CardTypeIndicators.No,
-            "expiration_date": "05/2014",
-            "options": {"verify_card": True}
-        })
-
-        credit_card = result.credit_card
-
-        self.assertEqual(CreditCard.Debit.No, credit_card.debit)
-        self.assertEqual(CreditCard.DurbinRegulated.No, credit_card.durbin_regulated)
-        self.assertEqual(CreditCard.Prepaid.No, credit_card.prepaid)
-        self.assertEqual(CreditCard.Payroll.No, credit_card.payroll)
-        self.assertEqual(CreditCard.Commercial.No, credit_card.commercial)
-        self.assertEqual(CreditCard.Healthcare.No, credit_card.healthcare)
-        self.assertEqual("MSB", credit_card.product_id)
-
-    def test_card_without_card_type_indicators(self):
-        customer = Customer.create().customer
-        result = CreditCard.create({
-            "customer_id": customer.id,
-            "number": CreditCardNumbers.CardTypeIndicators.Unknown,
-            "expiration_date": "05/2014",
-            "options": {"verify_card": True}
-        })
-
-        credit_card = result.credit_card
-
-        self.assertEqual(CreditCard.Debit.Unknown, credit_card.debit)
-        self.assertEqual(CreditCard.DurbinRegulated.Unknown, credit_card.durbin_regulated)
-        self.assertEqual(CreditCard.Prepaid.Unknown, credit_card.prepaid)
-        self.assertEqual(CreditCard.Payroll.Unknown, credit_card.payroll)
-        self.assertEqual(CreditCard.Commercial.Unknown, credit_card.commercial)
-        self.assertEqual(CreditCard.Healthcare.Unknown, credit_card.healthcare)
-        self.assertEqual(CreditCard.IssuingBank.Unknown, credit_card.issuing_bank)
-        self.assertEqual(CreditCard.CountryOfIssuance.Unknown, credit_card.country_of_issuance)
-        self.assertEquals(CreditCard.ProductId.Unknown, credit_card.product_id)
-
-    def test_card_with_account_type_debit(self):
-        customer = Customer.create().customer
-        result = CreditCard.create({
-            "customer_id": customer.id,
-            "number": CreditCardNumbers.Hiper,
-            "expiration_date": "05/2014",
-            "options": {
-                "verify_card": True,
-                "verification_merchant_account_id": "hiper_brl",
-                "verification_account_type": "debit"
-            }
-        })
-
-        self.assertEqual(result.is_success, True)
-        self.assertEqual("debit", result.credit_card.verifications[0]["credit_card"]["account_type"])
-
-        updated_result = CreditCard.update(result.credit_card.token, {
-            "options": {
-                "verify_card": True,
-                "verification_merchant_account_id": "hiper_brl",
-                "verification_account_type": "debit"
-            }
-        })
-
-        self.assertEqual(updated_result.is_success, True)
-        self.assertEqual("debit", updated_result.credit_card.verifications[1]["credit_card"]["account_type"])
-
-    def test_card_with_account_type_credit(self):
-        customer = Customer.create().customer
-        result = CreditCard.create({
-            "customer_id": customer.id,
-            "number": CreditCardNumbers.Hiper,
-            "expiration_date": "05/2014",
-            "options": {
-                "verify_card": True,
-                "verification_merchant_account_id": "hiper_brl",
-                "verification_account_type": "credit"
-            }
-        })
-
-        self.assertEqual(result.is_success, True)
-        self.assertEqual("credit", result.credit_card.verifications[0]["credit_card"]["account_type"])
-
-        updated_result = CreditCard.update(result.credit_card.token, {
-            "options": {
-                "verify_card": True,
-                "verification_merchant_account_id": "hiper_brl",
-                "verification_account_type": "credit"
-            }
-        })
-
-        self.assertEqual(updated_result.is_success, True)
-        self.assertEqual("credit", updated_result.credit_card.verifications[0]["credit_card"]["account_type"])
-
-    def test_card_with_account_type_credit_debit(self):
-        customer = Customer.create().customer
-        result = CreditCard.create({
-            "customer_id": customer.id,
-            "number": CreditCardNumbers.Hiper,
-            "expiration_date": "05/2014",
-            "options": {
-                "verify_card": True,
-                "verification_merchant_account_id": "hiper_brl",
-                "verification_account_type": "credit"
-            }
-        })
-
-        self.assertEqual(result.is_success, True)
-        self.assertEqual("credit", result.credit_card.verifications[0]["credit_card"]["account_type"])
-
-        updated_result = CreditCard.update(result.credit_card.token, {
-            "options": {
-                "verify_card": True,
-                "verification_merchant_account_id": "hiper_brl",
-                "verification_account_type": "debit"
-            }
-        })
-
-        self.assertEqual(updated_result.is_success, True)
-        self.assertEqual("debit", updated_result.credit_card.verifications[0]["credit_card"]["account_type"])
-        self.assertEqual("credit", updated_result.credit_card.verifications[1]["credit_card"]["account_type"])
diff --git a/tests/integration/test_credit_card_verification.py b/tests/integration/test_credit_card_verification.py
deleted file mode 100644
index ad3d0b5..0000000
--- a/tests/integration/test_credit_card_verification.py
+++ /dev/null
@@ -1,170 +0,0 @@
-from tests.test_helper import *
-from braintree.test.credit_card_numbers import CreditCardNumbers
-
-class TestCreditCardVerfication(unittest.TestCase):
-
-    def test_create_success(self):
-        result = CreditCardVerification.create({
-            "credit_card": {
-                "number": CreditCardNumbers.Visa,
-                "cardholder_name": "John Smith",
-                "expiration_date": "05/2012"
-        }})
-
-        self.assertTrue(result.is_success)
-        verification = result.verification
-        self.assertEqual("1000", verification.processor_response_code)
-        self.assertEqual(ProcessorResponseTypes.Approved, verification.processor_response_type)
-
-    def test_create_failure(self):
-        result = CreditCardVerification.create({
-            "credit_card": {
-                "number": CreditCardNumbers.FailsSandboxVerification.MasterCard,
-                "expiration_date": "05/2012"
-        }})
-
-        self.assertFalse(result.is_success)
-        verification = result.credit_card_verification
-        self.assertEqual("2000", verification.processor_response_code)
-        self.assertEqual(ProcessorResponseTypes.SoftDeclined, verification.processor_response_type)
-
-    def test_create_returns_validation_errors(self):
-        result = CreditCardVerification.create({
-            "credit_card": {
-                "number": CreditCardNumbers.FailsSandboxVerification.MasterCard,
-                "expiration_date": "05/2012"
-            },
-            "options": {"amount": "-10.00"}
-        })
-
-        self.assertFalse(result.is_success)
-
-        amount_errors = result.errors.for_object("verification").for_object("options").on("amount")
-        self.assertEqual(1, len(amount_errors))
-        self.assertEqual(ErrorCodes.Verification.Options.AmountCannotBeNegative, amount_errors[0].code)
-
-    def test_create_with_account_type_debit(self):
-        result = CreditCardVerification.create({
-            "credit_card": {
-                "number": CreditCardNumbers.Hiper,
-                "expiration_date": "10/2020",
-                "cvv": "737",
-            },
-            "options": {
-                "merchant_account_id": TestHelper.hiper_brl_merchant_account_id,
-                "account_type": "debit",
-            },
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertEqual("debit", result.verification.credit_card["account_type"])
-
-    def test_create_with_account_type_credit(self):
-        result = CreditCardVerification.create({
-            "credit_card": {
-                "number": CreditCardNumbers.Hiper,
-                "expiration_date": "10/2020",
-                "cvv": "737",
-            },
-            "options": {
-                "merchant_account_id": TestHelper.hiper_brl_merchant_account_id,
-                "account_type": "credit",
-            },
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertEqual("credit", result.verification.credit_card["account_type"])
-
-
-    def test_create_with_unsupported_account_type(self):
-        result = CreditCardVerification.create({
-            "credit_card": {
-                "number": CreditCardNumbers.Visa,
-                "cardholder_name": "John Smith",
-                "expiration_date": "05/2012"
-            },
-            "options": {
-                "account_type": "debit",
-            },
-        })
-
-        self.assertFalse(result.is_success)
-
-        account_type_errors = result.errors.for_object("verification").for_object("options").on("account_type")
-        self.assertEqual(1, len(account_type_errors))
-        self.assertEqual(ErrorCodes.Verification.Options.AccountTypeNotSupported , account_type_errors[0].code)
-
-    def test_create_with_invalid_account_type(self):
-        result = CreditCardVerification.create({
-            "credit_card": {
-                "number": CreditCardNumbers.Hiper,
-                "expiration_date": "10/2020",
-                "cvv": "737",
-            },
-            "options": {
-                "merchant_account_id": TestHelper.hiper_brl_merchant_account_id,
-                "account_type": "invalid",
-            },
-        })
-
-        self.assertFalse(result.is_success)
-
-        account_type_errors = result.errors.for_object("verification").for_object("options").on("account_type")
-        self.assertEqual(1, len(account_type_errors))
-        self.assertEqual(ErrorCodes.Verification.Options.AccountTypeIsInvalid, account_type_errors[0].code)
-
-    def test_find_with_verification_id(self):
-        customer = Customer.create({
-            "credit_card": {
-                "number": CreditCardNumbers.FailsSandboxVerification.MasterCard,
-                "expiration_date": "05/2012",
-                "cardholder_name": "Tom Smith",
-                "options": {"verify_card": True}
-        }})
-
-        created_verification = customer.credit_card_verification
-        found_verification = CreditCardVerification.find(created_verification.id)
-        self.assertEqual(created_verification, found_verification)
-
-    def test_verification_not_found(self):
-        self.assertRaises(NotFoundError, CreditCardVerification.find,
-          "invalid-id")
-
-    def test_card_type_indicators(self):
-        cardholder_name = "Tom %s" % random.randint(1, 10000)
-        Customer.create({"credit_card": {
-            "cardholder_name": cardholder_name,
-            "expiration_date": "10/2012",
-            "number": CreditCardNumbers.CardTypeIndicators.Unknown,
-            "options": {"verify_card": True}
-        }})
-        found_verifications = CreditCardVerification.search(
-            CreditCardVerificationSearch.credit_card_cardholder_name == cardholder_name
-        )
-
-        self.assertEqual(CreditCard.Prepaid.Unknown, found_verifications.first.credit_card['prepaid'])
-        self.assertEqual(CreditCard.Debit.Unknown, found_verifications.first.credit_card['debit'])
-        self.assertEqual(CreditCard.Commercial.Unknown, found_verifications.first.credit_card['commercial'])
-        self.assertEqual(CreditCard.Healthcare.Unknown, found_verifications.first.credit_card['healthcare'])
-        self.assertEqual(CreditCard.Payroll.Unknown, found_verifications.first.credit_card['payroll'])
-        self.assertEqual(CreditCard.DurbinRegulated.Unknown, found_verifications.first.credit_card['durbin_regulated'])
-        self.assertEqual(CreditCard.IssuingBank.Unknown, found_verifications.first.credit_card['issuing_bank'])
-        self.assertEqual(CreditCard.CountryOfIssuance.Unknown, found_verifications.first.credit_card['country_of_issuance'])
-        self.assertEqual(CreditCard.ProductId.Unknown, found_verifications.first.credit_card['product_id'])
-
-    def test_create_success_network_response_code_text(self):
-        result = CreditCardVerification.create({
-            "credit_card": {
-                "number": CreditCardNumbers.Visa,
-                "cardholder_name": "John Smith",
-                "expiration_date": "05/2012"
-            },
-        })
-
-        self.assertTrue(result.is_success)
-        verification = result.verification
-        self.assertEqual("1000", verification.processor_response_code)
-        self.assertEqual(ProcessorResponseTypes.Approved, verification.processor_response_type)
-        self.assertEqual("XX", verification.network_response_code)
-        self.assertEqual("sample network response text", verification.network_response_text)
-
diff --git a/tests/integration/test_credit_card_verification_search.py b/tests/integration/test_credit_card_verification_search.py
deleted file mode 100644
index b787262..0000000
--- a/tests/integration/test_credit_card_verification_search.py
+++ /dev/null
@@ -1,133 +0,0 @@
-from tests.test_helper import *
-from braintree.test.credit_card_numbers import CreditCardNumbers
-
-class TestVerificationSearch(unittest.TestCase):
-    def test_advanced_search_no_results(self):
-        collection = CreditCardVerification.search([
-            CreditCardVerificationSearch.credit_card_cardholder_name == "no such person"])
-        self.assertEqual(0, collection.maximum_size)
-
-    def test_search_on_verification_id(self):
-        customer_id = "%s" % random.randint(1, 10000)
-
-        result = Customer.create({
-            "id": customer_id,
-            "credit_card": {
-                "expiration_date": "10/2018",
-                "number": CreditCardNumbers.FailsSandboxVerification.Visa,
-                "options": {
-                    "verify_card": True
-                }
-            }
-        })
-        verification_id = result.credit_card_verification.id
-
-        found_verifications = CreditCardVerification.search(
-            CreditCardVerificationSearch.id == verification_id
-        )
-
-        self.assertEqual(1, found_verifications.maximum_size)
-        self.assertEqual(verification_id, found_verifications.first.id)
-
-    def test_all_text_fields(self):
-        email = "mark.a@example.com"
-        cardholder_name = "Tom %s" % random.randint(1, 10000)
-        customer_id = "%s" % random.randint(1, 10000)
-        expiration_date = "10/2012"
-        number = CreditCardNumbers.MasterCard
-        postal_code = "44444"
-
-        customer = Customer.create({
-            "id": customer_id,
-            "email": email,
-            "credit_card": {
-                "cardholder_name": cardholder_name,
-                "expiration_date": expiration_date,
-                "number": number,
-                "billing_address": {
-                    "postal_code": postal_code
-                },
-                "options": {
-                    "verify_card": True
-                }
-            }
-        }).customer
-
-        found_verifications = CreditCardVerification.search(
-            CreditCardVerificationSearch.credit_card_expiration_date == expiration_date,
-            CreditCardVerificationSearch.credit_card_cardholder_name == cardholder_name,
-            CreditCardVerificationSearch.credit_card_number == number,
-            CreditCardVerificationSearch.customer_email == email,
-            CreditCardVerificationSearch.customer_id == customer_id,
-            CreditCardVerificationSearch.billing_postal_code == postal_code
-        )
-
-        self.assertEqual(1, found_verifications.maximum_size)
-        self.assertEqual(customer.credit_cards[0].token, found_verifications.first.credit_card["token"])
-
-    def test_multiple_value_fields(self):
-        cardholder_name = "Tom %s" % random.randint(1, 10000)
-        number = CreditCardNumbers.FailsSandboxVerification.MasterCard
-        unsuccessful_result1 = Customer.create({"credit_card": {
-            "cardholder_name": cardholder_name,
-            "expiration_date": "10/2013",
-            "number": number,
-            "options": {"verify_card": True}
-        }})
-
-        cardholder_name = "Tom %s" % random.randint(1, 10000)
-        number = CreditCardNumbers.FailsSandboxVerification.Visa
-        unsuccessful_result2 = Customer.create({"credit_card": {
-            "cardholder_name": cardholder_name,
-            "expiration_date": "10/2012",
-            "number": number,
-            "options": {"verify_card": True}
-        }})
-
-        verification1 = unsuccessful_result1.credit_card_verification
-        verification2 = unsuccessful_result2.credit_card_verification
-
-        search_results = CreditCardVerification.search(
-                CreditCardVerificationSearch.ids.in_list([
-                    verification1.id, verification2.id]),
-                CreditCardVerificationSearch.credit_card_card_type.in_list([
-                    verification1.credit_card["card_type"], verification2.credit_card["card_type"]]),
-                CreditCardVerificationSearch.status.in_list([
-                    verification1.status, verification2.status])
-        )
-
-        self.assertEqual(2, search_results.maximum_size)
-
-    def test_range_field(self):
-        cardholder_name = "Tom %s" % random.randint(1, 10000)
-        number = CreditCardNumbers.FailsSandboxVerification.MasterCard
-        unsuccessful_result = Customer.create({"credit_card": {
-            "cardholder_name": cardholder_name,
-            "expiration_date": "10/2013",
-            "number": number,
-            "options": {"verify_card": True}
-        }})
-
-        created_verification = unsuccessful_result.credit_card_verification
-        created_time = created_verification.created_at
-        before_creation = created_time - timedelta(minutes=10)
-        after_creation = created_time + timedelta(minutes=10)
-        found_verifications = CreditCardVerification.search(
-                CreditCardVerificationSearch.id == created_verification.id,
-                CreditCardVerificationSearch.created_at.between(before_creation, after_creation))
-
-        self.assertEqual(1, found_verifications.maximum_size)
-
-        way_before_creation = created_time - timedelta(minutes=10)
-        just_before_creation = created_time - timedelta(minutes=1)
-        found_verifications = CreditCardVerification.search(
-                CreditCardVerificationSearch.id == created_verification.id,
-                CreditCardVerificationSearch.created_at.between(way_before_creation, just_before_creation))
-
-        self.assertEqual(0, found_verifications.maximum_size)
-
-        found_verifications = CreditCardVerification.search(
-                CreditCardVerificationSearch.id == created_verification.id,
-                CreditCardVerificationSearch.created_at == created_time)
-
-        self.assertEqual(1, found_verifications.maximum_size)
diff --git a/tests/integration/test_customer.py b/tests/integration/test_customer.py
deleted file mode 100644
index 5d838eb..0000000
--- a/tests/integration/test_customer.py
+++ /dev/null
@@ -1,1170 +0,0 @@
-# -*- coding: latin-1 -*-
-from tests.test_helper import *
-import braintree.test.venmo_sdk as venmo_sdk
-from braintree.test.nonces import Nonces
-
-class TestCustomer(unittest.TestCase):
-    def test_all(self):
-        collection = Customer.all()
-        self.assertTrue(collection.maximum_size > 100)
-        customer_ids = [c.id for c in collection.items]
-        self.assertEqual(collection.maximum_size, len(TestHelper.unique(customer_ids)))
-        self.assertEqual(Customer, type(collection.first))
-
-    def test_create(self):
-        result = Customer.create({
-            "first_name": "Joe",
-            "last_name": "Brown",
-            "company": "Fake Company",
-            "email": "joe@email.com",
-            "phone": "312.555.1234",
-            "fax": "614.555.5678",
-            "website": "www.email.com"
-        })
-
-        self.assertTrue(result.is_success)
-        customer = result.customer
-
-        self.assertEqual("Joe", customer.first_name)
-        self.assertEqual("Brown", customer.last_name)
-        self.assertEqual("Fake Company", customer.company)
-        self.assertEqual("joe@email.com", customer.email)
-        self.assertEqual("312.555.1234", customer.phone)
-        self.assertEqual("614.555.5678", customer.fax)
-        self.assertEqual("www.email.com", customer.website)
-        self.assertNotEqual(None, customer.id)
-        self.assertNotEqual(None, re.search(r"\A\d{6,}\Z", customer.id))
-
-    def test_create_unicode(self):
-        result = Customer.create({
-            "first_name": "Kimi",
-            "last_name": u"Räikkönen",
-            "company": "Fake Company",
-            "email": "joe@email.com",
-            "phone": "312.555.1234",
-            "fax": "614.555.5678",
-            "website": "www.email.com"
-        })
-
-        self.assertTrue(result.is_success)
-        customer = result.customer
-
-        self.assertEqual("Kimi", customer.first_name)
-        self.assertEqual(u"Räikkönen", customer.last_name)
-        self.assertEqual("Fake Company", customer.company)
-        self.assertEqual("joe@email.com", customer.email)
-        self.assertEqual("312.555.1234", customer.phone)
-        self.assertEqual("614.555.5678", customer.fax)
-        self.assertEqual("www.email.com", customer.website)
-        self.assertNotEqual(None, customer.id)
-
-    def test_create_with_device_session_id_and_fraud_merchant_id(self):
-        result = Customer.create({
-            "first_name": "Joe",
-            "last_name": "Brown",
-            "company": "Fake Company",
-            "email": "joe@email.com",
-            "phone": "312.555.1234",
-            "fax": "614.555.5678",
-            "website": "www.email.com",
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2010",
-                "cvv": "100",
-                "device_session_id": "abc123",
-                "fraud_merchant_id": "456"
-            }
-        })
-
-        self.assertTrue(result.is_success)
-
-    def test_create_with_risk_data_security_parameters(self):
-        result = Customer.create({
-            "first_name": "Joe",
-            "last_name": "Brown",
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2010",
-                "options": {
-                    "verify_card": True,
-                }
-            },
-            "risk_data": {
-                "customer_browser": "IE7",
-                "customer_ip": "192.168.0.1"
-            }
-        })
-
-        self.assertTrue(result.is_success)
-
-
-    def test_create_and_update_with_verification_account_type(self):
-        result_with_account_type_credit = Customer.create({
-            "first_name": "Joe",
-            "last_name": "Brown",
-            "credit_card": {
-                "number": CreditCardNumbers.Hiper,
-                "expiration_date": "05/2010",
-                "options": {
-                    "verify_card": True,
-                    "verification_merchant_account_id": "hiper_brl",
-                    "verification_account_type": "credit",
-                    }
-                },
-            })
-
-        update_with_account_type_credit = Customer.update(result_with_account_type_credit.customer.id, {
-            "credit_card": {
-                "number": CreditCardNumbers.Hiper,
-                "expiration_date": "05/2010",
-                "options": {
-                    "verification_account_type": "credit",
-                    }
-                },
-            })
-
-        result_with_account_type_debit = Customer.create({
-            "first_name": "Joe",
-            "last_name": "Brown",
-            "credit_card": {
-                "number": CreditCardNumbers.Hiper,
-                "expiration_date": "05/2010",
-                "options": {
-                    "verify_card": True,
-                    "verification_merchant_account_id": "hiper_brl",
-                    "verification_account_type": "debit",
-                    }
-                },
-            })
-
-        update_with_account_type_debit = Customer.update(result_with_account_type_debit.customer.id, {
-            "credit_card": {
-                "number": CreditCardNumbers.Hiper,
-                "expiration_date": "05/2010",
-                "options": {
-                    "verification_account_type": "debit",
-                    }
-                },
-            })
-
-        self.assertTrue(result_with_account_type_credit.is_success)
-        self.assertTrue(result_with_account_type_debit.is_success)
-        self.assertTrue(update_with_account_type_credit.is_success)
-        self.assertTrue(update_with_account_type_debit.is_success)
-
-    def test_create_using_access_token(self):
-        gateway = BraintreeGateway(
-            client_id="client_id$development$integration_client_id",
-            client_secret="client_secret$development$integration_client_secret",
-            environment=Environment.Development
-        )
-
-        code = TestHelper.create_grant(gateway, {
-            "merchant_public_id": "integration_merchant_id",
-            "scope": "read_write"
-        })
-
-        result = gateway.oauth.create_token_from_code({
-            "code": code
-        })
-
-        gateway = BraintreeGateway(
-            access_token=result.credentials.access_token,
-            environment=Environment.Development
-        )
-
-        result = gateway.customer.create({
-            "first_name": "Joe",
-            "last_name": "Brown"
-        })
-
-        self.assertTrue(result.is_success)
-        customer = result.customer
-
-        self.assertEqual("Joe", customer.first_name)
-
-    def test_create_with_unicode(self):
-        result = Customer.create({
-            "first_name": u"Joe<&>",
-            "last_name": u"G\u1F00t\u1F18s",
-            "company": "Fake Company",
-            "email": "joe@email.com",
-            "phone": "312.555.1234",
-            "fax": "614.555.5678",
-            "website": "www.email.com"
-        })
-
-        self.assertTrue(result.is_success)
-        customer = result.customer
-
-        self.assertEqual(u"Joe<&>", customer.first_name)
-        self.assertEqual(u"G\u1f00t\u1F18s", customer.last_name)
-        self.assertEqual("Fake Company", customer.company)
-        self.assertEqual("joe@email.com", customer.email)
-        self.assertEqual("312.555.1234", customer.phone)
-        self.assertEqual("614.555.5678", customer.fax)
-        self.assertEqual("www.email.com", customer.website)
-        self.assertNotEqual(None, customer.id)
-        self.assertNotEqual(None, re.search(r"\A\d{6,}\Z", customer.id))
-
-        found_customer = Customer.find(customer.id)
-        self.assertEqual(u"G\u1f00t\u1F18s", found_customer.last_name)
-
-    def test_create_with_apple_pay_nonce(self):
-        result = Customer.create({"payment_method_nonce": Nonces.ApplePayVisa})
-        self.assertTrue(result.is_success)
-
-        customer = result.customer
-        self.assertEqual(1, len(customer.apple_pay_cards))
-        self.assertIsInstance(customer.apple_pay_cards[0], ApplePayCard)
-
-    def test_create_with_three_d_secure_nonce(self):
-        result = Customer.create({
-            "payment_method_nonce": Nonces.ThreeDSecureVisaFullAuthentication,
-            "credit_card": {
-                "options": {
-                    "verify_card": True,
-                    },
-            },
-        })
-
-        self.assertTrue(result.is_success)
-
-        three_d_secure_info = result.customer.payment_methods[0].verification.three_d_secure_info
-
-        self.assertEqual("Y", three_d_secure_info.enrolled)
-        self.assertEqual("authenticate_successful", three_d_secure_info.status)
-        self.assertEqual(True, three_d_secure_info.liability_shifted)
-        self.assertEqual(True, three_d_secure_info.liability_shift_possible)
-        self.assertEqual("cavv_value", three_d_secure_info.cavv)
-        self.assertEqual("xid_value", three_d_secure_info.xid)
-        self.assertEqual(None, three_d_secure_info.ds_transaction_id)
-        self.assertEqual("05", three_d_secure_info.eci_flag)
-        self.assertEqual("1.0.2", three_d_secure_info.three_d_secure_version)
-
-    def test_create_with_android_pay_proxy_card_nonce(self):
-        result = Customer.create({"payment_method_nonce": Nonces.AndroidPayCardDiscover})
-        self.assertTrue(result.is_success)
-
-        customer = result.customer
-        self.assertEqual(1, len(customer.android_pay_cards))
-        self.assertIsInstance(customer.android_pay_cards[0], AndroidPayCard)
-
-    def test_create_with_android_pay_network_token_nonce(self):
-        result = Customer.create({"payment_method_nonce": Nonces.AndroidPayCardMasterCard})
-        self.assertTrue(result.is_success)
-
-        customer = result.customer
-        self.assertEqual(1, len(customer.android_pay_cards))
-        self.assertIsInstance(customer.android_pay_cards[0], AndroidPayCard)
-
-    def test_create_with_amex_express_checkout_card_nonce(self):
-        result = Customer.create({"payment_method_nonce": Nonces.AmexExpressCheckoutCard})
-        self.assertTrue(result.is_success)
-
-        customer = result.customer
-        self.assertEqual(1, len(customer.amex_express_checkout_cards))
-        self.assertIsInstance(customer.amex_express_checkout_cards[0], AmexExpressCheckoutCard)
-
-    def test_create_with_venmo_account_nonce(self):
-        result = Customer.create({"payment_method_nonce": Nonces.VenmoAccount})
-        self.assertTrue(result.is_success)
-
-        customer = result.customer
-        self.assertEqual(1, len(customer.venmo_accounts))
-        self.assertIsInstance(customer.venmo_accounts[0], VenmoAccount)
-
-    def test_create_with_us_bank_account_nonce(self):
-        result = Customer.create({
-            "payment_method_nonce": TestHelper.generate_valid_us_bank_account_nonce(),
-            "credit_card": {
-                "options": {
-                    "verification_merchant_account_id": TestHelper.us_bank_merchant_account_id
-                }
-            }
-        })
-        self.assertTrue(result.is_success)
-
-        customer = result.customer
-        self.assertEqual(1, len(customer.us_bank_accounts))
-        self.assertIsInstance(customer.us_bank_accounts[0], UsBankAccount)
-
-    def test_create_with_paypal_future_payments_nonce(self):
-        result = Customer.create({"payment_method_nonce": Nonces.PayPalFuturePayment})
-        self.assertTrue(result.is_success)
-
-        customer = result.customer
-        self.assertEqual(1, len(customer.paypal_accounts))
-        self.assertIsInstance(customer.paypal_accounts[0], PayPalAccount)
-
-    def test_create_with_paypal_order_payment_nonce(self):
-        http = ClientApiHttp.create()
-        status_code, payment_method_nonce = http.get_paypal_nonce({
-            "intent": "order",
-            "payment-token": "fake-paypal-payment-token",
-            "payer-id": "fake-paypal-payer-id"
-        })
-
-        result = Customer.create({"payment_method_nonce": payment_method_nonce})
-        self.assertTrue(result.is_success)
-
-        customer = result.customer
-        self.assertEqual(1, len(customer.paypal_accounts))
-        self.assertIsInstance(customer.paypal_accounts[0], PayPalAccount)
-
-    def test_create_with_paypal_order_payment_nonce_and_paypal_options(self):
-        http = ClientApiHttp.create()
-        status_code, payment_method_nonce = http.get_paypal_nonce({
-            "intent": "order",
-            "payment-token": "fake-paypal-payment-token",
-            "payer-id": "fake-paypal-payer-id"
-        })
-
-        result = Customer.create({
-            "payment_method_nonce": payment_method_nonce,
-            "options": {
-                "paypal": {
-                    "payee_email": "payee@example.com",
-                    "order_id": "merchant-order-id",
-                    "custom_field": "custom merchant field",
-                    "description": "merchant description",
-                    "amount": "1.23",
-                    "shipping": {
-                        "first_name": "Andrew",
-                        "last_name": "Mason",
-                        "company": "Braintree",
-                        "street_address": "456 W Main St",
-                        "extended_address": "Apt 2F",
-                        "locality": "Bartlett",
-                        "region": "IL",
-                        "postal_code": "60103",
-                        "country_name": "Mexico",
-                        "country_code_alpha2": "MX",
-                        "country_code_alpha3": "MEX",
-                        "country_code_numeric": "484"
-                    },
-                },
-            },
-        })
-        self.assertTrue(result.is_success)
-
-        customer = result.customer
-        self.assertEqual(1, len(customer.paypal_accounts))
-        self.assertIsInstance(customer.paypal_accounts[0], PayPalAccount)
-
-    def test_create_with_paypal_one_time_nonce_fails(self):
-        result = Customer.create({"payment_method_nonce": Nonces.PayPalOneTimePayment})
-        self.assertFalse(result.is_success)
-
-        paypal_account_errors = result.errors.for_object("customer").for_object("paypal_account").on("base")
-        self.assertEqual(1, len(paypal_account_errors))
-        self.assertEqual(ErrorCodes.PayPalAccount.CannotVaultOneTimeUsePayPalAccount, paypal_account_errors[0].code)
-
-    def test_create_with_no_attributes(self):
-        result = Customer.create()
-        self.assertTrue(result.is_success)
-        self.assertNotEqual(None, result.customer.id)
-
-    def test_create_with_special_chars(self):
-        result = Customer.create({"first_name": "XML Chars <>&'\""})
-        self.assertTrue(result.is_success)
-        self.assertEqual("XML Chars <>&'\"", result.customer.first_name)
-
-    def test_create_returns_an_error_response_if_invalid(self):
-        result = Customer.create({
-            "email": "@invalid.com",
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2010",
-                "billing_address": {
-                    "country_code_alpha2": "MX",
-                    "country_code_alpha3": "USA"
-                }
-            }
-        })
-
-        self.assertFalse(result.is_success)
-
-        customer_email_errors = result.errors.for_object("customer").on("email")
-        self.assertEqual(1, len(customer_email_errors))
-        self.assertEqual(ErrorCodes.Customer.EmailIsInvalid, customer_email_errors[0].code)
-
-        billing_address_errors = result.errors.for_object("customer").for_object("credit_card").for_object("billing_address").on("base")
-        self.assertEqual(1, len(billing_address_errors))
-        self.assertEqual(ErrorCodes.Address.InconsistentCountry, billing_address_errors[0].code)
-
-    def test_create_customer_and_payment_method_at_the_same_time(self):
-        result = Customer.create({
-            "first_name": "Mike",
-            "last_name": "Jones",
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2010",
-                "cvv": "100"
-            }
-        })
-
-        self.assertTrue(result.is_success)
-
-        customer = result.customer
-        self.assertEqual("Mike", customer.first_name)
-        self.assertEqual("Jones", customer.last_name)
-
-        credit_card = customer.credit_cards[0]
-        self.assertEqual("411111", credit_card.bin)
-        self.assertEqual("1111", credit_card.last_4)
-        self.assertEqual("05/2010", credit_card.expiration_date)
-
-    def test_create_customer_and_verify_payment_method(self):
-        result = Customer.create({
-            "first_name": "Mike",
-            "last_name": "Jones",
-            "credit_card": {
-                "number": "4000111111111115",
-                "expiration_date": "05/2010",
-                "cvv": "100",
-                "options": {"verify_card": True}
-            }
-        })
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(CreditCardVerification.Status.ProcessorDeclined, result.credit_card_verification.status)
-
-    def test_create_customer_and_verify_payment_method_with_verification_amount(self):
-        result = Customer.create({
-            "first_name": "Mike",
-            "last_name": "Jones",
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2010",
-                "cvv": "100",
-                "options": {"verify_card": True, "verification_amount": "6.00"}
-            }
-        })
-
-        self.assertTrue(result.is_success)
-
-    def test_create_customer_with_check_duplicate_payment_method(self):
-        attributes = {
-            "first_name": "Mike",
-            "last_name": "Jones",
-            "credit_card": {
-                "number": "4000111111111115",
-                "expiration_date": "05/2010",
-                "cvv": "100",
-                "options": {"fail_on_duplicate_payment_method": True}
-            }
-        }
-
-        Customer.create(attributes)
-        result = Customer.create(attributes)
-
-        self.assertFalse(result.is_success)
-        self.assertEqual("Duplicate card exists in the vault.", result.message)
-
-        card_number_errors = result.errors.for_object("customer").for_object("credit_card").on("number")
-        self.assertEqual(1, len(card_number_errors))
-        self.assertEqual(ErrorCodes.CreditCard.DuplicateCardExists, card_number_errors[0].code)
-
-    def test_create_customer_with_payment_method_and_billing_address(self):
-        result = Customer.create({
-            "first_name": "Mike",
-            "last_name": "Jones",
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2010",
-                "cvv": "100",
-                "billing_address": {
-                    "street_address": "123 Abc Way",
-                    "locality": "Chicago",
-                    "region": "Illinois",
-                    "postal_code": "60622",
-                    "country_code_alpha2": "US",
-                    "country_code_alpha3": "USA",
-                    "country_code_numeric": "840",
-                    "country_name": "United States of America"
-                }
-            }
-        })
-
-        self.assertTrue(result.is_success)
-
-        customer = result.customer
-        self.assertEqual("Mike", customer.first_name)
-        self.assertEqual("Jones", customer.last_name)
-
-        address = customer.credit_cards[0].billing_address
-        self.assertEqual("123 Abc Way", address.street_address)
-        self.assertEqual("Chicago", address.locality)
-        self.assertEqual("Illinois", address.region)
-        self.assertEqual("60622", address.postal_code)
-        self.assertEqual("US", address.country_code_alpha2)
-        self.assertEqual("USA", address.country_code_alpha3)
-        self.assertEqual("840", address.country_code_numeric)
-        self.assertEqual("United States of America", address.country_name)
-
-    def test_create_with_customer_fields(self):
-        result = Customer.create({
-            "first_name": "Mike",
-            "last_name": "Jones",
-            "custom_fields": {
-                "store_me": "custom value"
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertEqual("custom value", result.customer.custom_fields["store_me"])
-
-    def test_create_returns_nested_errors(self):
-        result = Customer.create({
-            "email": "invalid",
-            "credit_card": {
-                "number": "invalid",
-                "billing_address": {
-                    "country_name": "invalid"
-                }
-            }
-        })
-
-        self.assertFalse(result.is_success)
-
-        email_errors = result.errors.for_object("customer").on("email")
-        self.assertEqual(1, len(email_errors))
-        self.assertEqual(ErrorCodes.Customer.EmailIsInvalid, email_errors[0].code)
-
-        card_number_errors = result.errors.for_object("customer").for_object("credit_card").on("number")
-        self.assertEqual(1, len(card_number_errors))
-        self.assertEqual(ErrorCodes.CreditCard.NumberHasInvalidLength, card_number_errors[0].code)
-
-        country_name_errors = result.errors.for_object("customer").for_object("credit_card").for_object("billing_address").on("country_name")
-        self.assertEqual(1, len(country_name_errors))
-        self.assertEqual(ErrorCodes.Address.CountryNameIsNotAccepted, country_name_errors[0].code)
-
-    def test_create_returns_errors_if_custom_fields_are_not_registered(self):
-        result = Customer.create({
-            "first_name": "Jack",
-            "last_name": "Kennedy",
-            "custom_fields": {
-                "spouse_name": "Jacqueline"
-            }
-        })
-
-        self.assertFalse(result.is_success)
-
-        custom_fields_errors = result.errors.for_object("customer").on("custom_fields")
-        self.assertEqual(1, len(custom_fields_errors))
-        self.assertEqual(ErrorCodes.Customer.CustomFieldIsInvalid, custom_fields_errors[0].code)
-
-    def test_create_with_venmo_sdk_session(self):
-        result = Customer.create({
-            "first_name": "Jack",
-            "last_name": "Kennedy",
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2010",
-                "options": {
-                    "venmo_sdk_session": venmo_sdk.Session
-                }
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertFalse(result.customer.credit_cards[0].venmo_sdk)
-
-    def test_create_with_venmo_sdk_payment_method_code(self):
-        result = Customer.create({
-            "first_name": "Jack",
-            "last_name": "Kennedy",
-            "credit_card": {
-                "venmo_sdk_payment_method_code": venmo_sdk.generate_test_payment_method_code("4111111111111111")
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertEqual("411111", result.customer.credit_cards[0].bin)
-
-    def test_create_with_payment_method_nonce(self):
-        config = Configuration.instantiate()
-        authorization_fingerprint = json.loads(TestHelper.generate_decoded_client_token())["authorizationFingerprint"]
-        http = ClientApiHttp(config, {
-            "authorization_fingerprint": authorization_fingerprint,
-            "shared_customer_identifier": "fake_identifier",
-            "shared_customer_identifier_type": "testing"
-        })
-        _, response = http.add_card({
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_month": "11",
-                "expiration_year": "2099",
-            },
-            "share": True
-        })
-        nonce = json.loads(response)["creditCards"][0]["nonce"]
-
-        result = Customer.create({
-            "credit_card": {
-                "payment_method_nonce": nonce
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertEqual("411111", result.customer.credit_cards[0].bin)
-
-    def test_delete_with_valid_customer(self):
-        customer = Customer.create().customer
-        result = Customer.delete(customer.id)
-
-        self.assertTrue(result.is_success)
-
-    @raises(NotFoundError)
-    def test_delete_with_invalid_customer(self):
-        customer = Customer.create().customer
-        Customer.delete(customer.id)
-        Customer.delete(customer.id)
-
-    def test_find_with_valid_customer(self):
-        customer = Customer.create({
-            "first_name": "Joe",
-            "last_name": "Cool"
-        }).customer
-
-        found_customer = Customer.find(customer.id)
-        self.assertEqual(customer.id, found_customer.id)
-        self.assertEqual(customer.first_name, found_customer.first_name)
-        self.assertEqual(customer.last_name, found_customer.last_name)
-
-    def test_find_customer_with_us_bank_account(self):
-        customer = Customer.create({
-            "payment_method_nonce": TestHelper.generate_valid_us_bank_account_nonce(),
-            "credit_card": {
-                "options": {
-                    "verification_merchant_account_id": TestHelper.us_bank_merchant_account_id
-                }
-            }
-        }).customer
-
-        found_customer = Customer.find(customer.id)
-        self.assertEqual(customer.id, found_customer.id)
-        self.assertEqual(customer.first_name, found_customer.first_name)
-        self.assertEqual(customer.last_name, found_customer.last_name)
-        self.assertEqual(1, len(found_customer.us_bank_accounts))
-        self.assertIsInstance(found_customer.us_bank_accounts[0], UsBankAccount)
-
-    @raises_with_regexp(NotFoundError, "customer with id 'badid' not found")
-    def test_find_with_invalid_customer(self):
-        Customer.find("badid")
-
-    def test_find_customer_with_all_filterable_associations_filtered_out(self):
-        customer = Customer.create({
-            "custom_fields": {
-                "store_me": "custom value"
-            }
-        }).customer
-        credit_card = CreditCard.create({
-            "customer_id": customer.id,
-            "number": "4111111111111111",
-            "expiration_date": "05/2014",
-            "billing_address": {
-                "street_address": "123 Abc Way",
-                "locality": "Chicago",
-                "region": "Illinois",
-                "postal_code": "60622",
-                "country_name": "United States of America"
-            }
-        }).credit_card
-        subscription_id = "id_" + str(random.randint(1, 1000000))
-        subscription = Subscription.create({
-            "id": subscription_id,
-            "plan_id": "integration_trialless_plan",
-            "payment_method_token": credit_card.token,
-            "price": Decimal("1.00")
-        }).subscription
-
-        found_customer = Customer.find(customer.id, "customernoassociations")
-        self.assertEqual(len(found_customer.credit_cards), 0)
-        self.assertEqual(len(found_customer.payment_methods), 0)
-        self.assertEqual(len(found_customer.addresses), 0)
-        self.assertEqual(len(found_customer.custom_fields), 0)
-
-    def test_find_customer_with_nested_filterable_associations_filtered_out(self):
-        customer = Customer.create({
-            "custom_fields": {
-                "store_me": "custom value"
-            }
-        }).customer
-        credit_card = CreditCard.create({
-            "customer_id": customer.id,
-            "number": "4111111111111111",
-            "expiration_date": "05/2014",
-            "billing_address": {
-                "street_address": "123 Abc Way",
-                "locality": "Chicago",
-                "region": "Illinois",
-                "postal_code": "60622",
-                "country_name": "United States of America"
-            }
-        }).credit_card
-        subscription_id = "id_" + str(random.randint(1, 1000000))
-        subscription = Subscription.create({
-            "id": subscription_id,
-            "plan_id": "integration_trialless_plan",
-            "payment_method_token": credit_card.token,
-            "price": Decimal("1.00")
-        }).subscription
-
-        found_customer = Customer.find(customer.id, "customertoplevelassociations")
-        self.assertEqual(len(found_customer.credit_cards), 1)
-        self.assertEqual(len(found_customer.credit_cards[0].subscriptions), 0)
-        self.assertEqual(len(found_customer.payment_methods), 1)
-        self.assertEqual(len(found_customer.payment_methods[0].subscriptions), 0)
-        self.assertEqual(len(found_customer.addresses), 1)
-        self.assertEqual(len(found_customer.custom_fields), 1)
-
-    def test_update_with_valid_options(self):
-        customer = Customer.create({
-            "first_name": "Joe",
-            "last_name": "Brown",
-            "company": "Fake Company",
-            "email": "joe@email.com",
-            "phone": "312.555.5555",
-            "fax": "614.555.5555",
-            "website": "www.email.com"
-        }).customer
-
-        result = Customer.update(customer.id, {
-            "first_name": "Joe",
-            "last_name": "Brown",
-            "company": "Fake Company",
-            "email": "joe@email.com",
-            "phone": "312.555.1234",
-            "fax": "614.555.5678",
-            "website": "www.email.com"
-        })
-
-        self.assertTrue(result.is_success)
-        customer = result.customer
-
-        self.assertEqual("Joe", customer.first_name)
-        self.assertEqual("Brown", customer.last_name)
-        self.assertEqual("Fake Company", customer.company)
-        self.assertEqual("joe@email.com", customer.email)
-        self.assertEqual("312.555.1234", customer.phone)
-        self.assertEqual("614.555.5678", customer.fax)
-        self.assertEqual("www.email.com", customer.website)
-        self.assertNotEqual(None, customer.id)
-        self.assertNotEqual(None, re.search(r"\A\d{6,}\Z", customer.id))
-
-    def test_update_with_default_payment_method(self):
-        customer = Customer.create({
-            "first_name": "Joe",
-            "last_name": "Brown",
-        }).customer
-
-        token1 = str(random.randint(1, 1000000))
-
-        payment_method1 = PaymentMethod.create({
-            "customer_id": customer.id,
-            "payment_method_nonce": Nonces.TransactableVisa,
-            "token": token1
-        }).payment_method
-
-        payment_method1 = PaymentMethod.find(payment_method1.token)
-        self.assertTrue(payment_method1.default)
-
-        token2 = str(random.randint(1, 1000000))
-
-        payment_method2 = PaymentMethod.create({
-            "customer_id": customer.id,
-            "payment_method_nonce": Nonces.TransactableMasterCard,
-            "token": token2
-        }).payment_method
-
-        Customer.update(customer.id, {
-            "default_payment_method_token": payment_method2.token
-        })
-
-        payment_method2 = PaymentMethod.find(payment_method2.token)
-        self.assertTrue(payment_method2.default)
-
-    def test_update_with_default_payment_method_in_options(self):
-        customer = Customer.create({
-            "first_name": "Joe",
-            "last_name": "Brown",
-        }).customer
-
-        token1 = str(random.randint(1, 1000000))
-
-        payment_method1 = PaymentMethod.create({
-            "customer_id": customer.id,
-            "payment_method_nonce": Nonces.TransactableVisa,
-            "token": token1
-        }).payment_method
-
-        payment_method1 = PaymentMethod.find(payment_method1.token)
-        self.assertTrue(payment_method1.default)
-
-        token2 = str(random.randint(1, 1000000))
-
-        payment_method2 = PaymentMethod.create({
-            "customer_id": customer.id,
-            "payment_method_nonce": Nonces.TransactableMasterCard,
-            "token": token2
-        }).payment_method
-
-        Customer.update(customer.id, {
-            "credit_card": {
-                "options": {
-                    "update_existing_token": token2,
-                    "make_default": True
-                    }
-                }
-            })
-
-        payment_method2 = PaymentMethod.find(payment_method2.token)
-        self.assertTrue(payment_method2.default)
-
-    def test_update_with_nested_values(self):
-        customer = Customer.create({
-            "first_name": "Joe",
-            "last_name": "Brown",
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "10/10",
-                "billing_address": {
-                    "postal_code": "11111"
-                }
-            }
-        }).customer
-        credit_card = customer.credit_cards[0]
-        address = credit_card.billing_address
-
-        updated_customer = Customer.update(customer.id, {
-            "first_name": "Joe",
-            "last_name": "Brown",
-            "credit_card": {
-                "expiration_date": "12/12",
-                "options": {
-                    "update_existing_token": credit_card.token
-                },
-                "billing_address": {
-                    "postal_code": "44444",
-                    "country_code_alpha2": "US",
-                    "country_code_alpha3": "USA",
-                    "country_code_numeric": "840",
-                    "country_name": "United States of America",
-                    "options": {
-                        "update_existing": True
-                    }
-                }
-            }
-        }).customer
-        updated_credit_card = CreditCard.find(credit_card.token)
-        updated_address = Address.find(customer.id, address.id)
-
-        self.assertEqual("Joe", updated_customer.first_name)
-        self.assertEqual("Brown", updated_customer.last_name)
-        self.assertEqual("12/2012", updated_credit_card.expiration_date)
-        self.assertEqual("44444", updated_address.postal_code)
-        self.assertEqual("US", updated_address.country_code_alpha2)
-        self.assertEqual("USA", updated_address.country_code_alpha3)
-        self.assertEqual("840", updated_address.country_code_numeric)
-        self.assertEqual("United States of America", updated_address.country_name)
-
-    def test_update_with_nested_billing_address_id(self):
-        customer = Customer.create().customer
-        address = Address.create({
-            "customer_id": customer.id,
-            "postal_code": "11111"
-        }).address
-
-        updated_customer = Customer.update(customer.id, {
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "12/12",
-                "billing_address_id": address.id
-            }
-        }).customer
-
-        credit_card = updated_customer.credit_cards[0]
-
-        self.assertEqual(address.id, credit_card.billing_address.id)
-        self.assertEqual("11111", credit_card.billing_address.postal_code)
-
-    def test_update_with_invalid_options(self):
-        customer = Customer.create({
-            "first_name": "Joe",
-            "last_name": "Brown",
-            "company": "Fake Company",
-            "email": "joe@email.com",
-            "phone": "312.555.5555",
-            "fax": "614.555.5555",
-            "website": "www.email.com"
-        }).customer
-
-        result = Customer.update(customer.id, {
-            "email": "@email.com",
-        })
-
-        self.assertFalse(result.is_success)
-
-        email_errors = result.errors.for_object("customer").on("email")
-        self.assertEqual(1, len(email_errors))
-        self.assertEqual(ErrorCodes.Customer.EmailIsInvalid, email_errors[0].code)
-
-    def test_update_with_paypal_future_payments_nonce(self):
-        customer = Customer.create().customer
-
-        result = Customer.update(customer.id, {
-            "payment_method_nonce": Nonces.PayPalFuturePayment
-        })
-        self.assertTrue(result.is_success)
-
-        customer = result.customer
-        self.assertNotEqual(None, customer.paypal_accounts[0])
-
-    def test_update_with_paypal_one_time_nonce_fails(self):
-        customer = Customer.create().customer
-        result = Customer.update(customer.id, {
-            "payment_method_nonce": Nonces.PayPalOneTimePayment
-        })
-        self.assertFalse(result.is_success)
-
-        paypal_account_errors = result.errors.for_object("customer").for_object("paypal_account").on("base")
-        self.assertEqual(1, len(paypal_account_errors))
-        self.assertEqual(ErrorCodes.PayPalAccount.CannotVaultOneTimeUsePayPalAccount, paypal_account_errors[0].code)
-
-
-    def test_update_with_paypal_order_nonce(self):
-        customer = Customer.create().customer
-
-        http = ClientApiHttp.create()
-        status_code, payment_method_nonce = http.get_paypal_nonce({
-            "intent": "order",
-            "payment-token": "fake-paypal-payment-token",
-            "payer-id": "fake-paypal-payer-id"
-        })
-
-        result = Customer.update(customer.id, {
-            "payment_method_nonce": payment_method_nonce,
-            "options": {
-                "paypal": {
-                    "payee_email": "payee@example.com",
-                    "order_id": "merchant-order-id",
-                    "custom_field": "custom merchant field",
-                    "description": "merchant description",
-                    "amount": "1.23",
-                    "shipping": {
-                        "first_name": "Andrew",
-                        "last_name": "Mason",
-                        "company": "Braintree",
-                        "street_address": "456 W Main St",
-                        "extended_address": "Apt 2F",
-                        "locality": "Bartlett",
-                        "region": "IL",
-                        "postal_code": "60103",
-                        "country_name": "Mexico",
-                        "country_code_alpha2": "MX",
-                        "country_code_alpha3": "MEX",
-                        "country_code_numeric": "484"
-                    },
-                },
-            },
-        })
-
-        self.assertTrue(result.is_success)
-        customer = result.customer
-        self.assertNotEqual(None, customer.paypal_accounts[0])
-        self.assertEqual(1, len(customer.paypal_accounts))
-        self.assertIsInstance(customer.paypal_accounts[0], PayPalAccount)
-
-    def test_update_with_nested_verification_amount(self):
-        customer = Customer.create({
-            "first_name": "Joe",
-            "last_name": "Brown",
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "10/10",
-                "billing_address": {
-                    "postal_code": "11111"
-                }
-            }
-        }).customer
-
-        result = Customer.update(customer.id, {
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "10/10",
-                "options": {
-                    "verify_card": True,
-                    "verification_amount": "2.00"
-                },
-            }
-        })
-
-        self.assertTrue(result.is_success)
-
-    def test_create_from_transparent_redirect_with_successful_result(self):
-        tr_data = {
-            "customer": {
-                "first_name": "John",
-                "last_name": "Doe",
-                "company": "Doe Co",
-            }
-        }
-        post_params = {
-            "tr_data": Customer.tr_data_for_create(tr_data, "http://example.com/path"),
-            "customer[email]": "john@doe.com",
-            "customer[phone]": "312.555.2323",
-            "customer[fax]": "614.555.5656",
-            "customer[website]": "www.johndoe.com",
-            "customer[credit_card][number]": "4111111111111111",
-            "customer[credit_card][expiration_date]": "05/2012",
-            "customer[credit_card][billing_address][country_code_alpha2]": "MX",
-            "customer[credit_card][billing_address][country_code_alpha3]": "MEX",
-            "customer[credit_card][billing_address][country_code_numeric]": "484",
-            "customer[credit_card][billing_address][country_name]": "Mexico",
-        }
-
-        query_string = TestHelper.simulate_tr_form_post(post_params, Customer.transparent_redirect_create_url())
-        result = Customer.confirm_transparent_redirect(query_string)
-        self.assertTrue(result.is_success)
-        customer = result.customer
-        self.assertEqual("John", customer.first_name)
-        self.assertEqual("Doe", customer.last_name)
-        self.assertEqual("Doe Co", customer.company)
-        self.assertEqual("john@doe.com", customer.email)
-        self.assertEqual("312.555.2323", customer.phone)
-        self.assertEqual("614.555.5656", customer.fax)
-        self.assertEqual("www.johndoe.com", customer.website)
-        self.assertEqual("05/2012", customer.credit_cards[0].expiration_date)
-        self.assertEqual("MX", customer.credit_cards[0].billing_address.country_code_alpha2)
-        self.assertEqual("MEX", customer.credit_cards[0].billing_address.country_code_alpha3)
-        self.assertEqual("484", customer.credit_cards[0].billing_address.country_code_numeric)
-        self.assertEqual("Mexico", customer.credit_cards[0].billing_address.country_name)
-
-    def test_create_from_transparent_redirect_with_error_result(self):
-        tr_data = {
-            "customer": {
-                "company": "Doe Co",
-            }
-        }
-        post_params = {
-            "tr_data": Customer.tr_data_for_create(tr_data, "http://example.com/path"),
-            "customer[email]": "john#doe.com",
-        }
-
-        query_string = TestHelper.simulate_tr_form_post(post_params, Customer.transparent_redirect_create_url())
-        result = Customer.confirm_transparent_redirect(query_string)
-        self.assertFalse(result.is_success)
-
-        email_errors = result.errors.for_object("customer").on("email")
-        self.assertEqual(1, len(email_errors))
-        self.assertEqual(ErrorCodes.Customer.EmailIsInvalid, email_errors[0].code)
-
-    def test_update_from_transparent_redirect_with_successful_result(self):
-        customer = Customer.create({
-            "first_name": "Jane",
-        }).customer
-
-        tr_data = {
-            "customer_id": customer.id,
-            "customer": {
-                "first_name": "John",
-            }
-        }
-        post_params = {
-            "tr_data": Customer.tr_data_for_update(tr_data, "http://example.com/path"),
-            "customer[email]": "john@doe.com",
-        }
-
-        query_string = TestHelper.simulate_tr_form_post(post_params, Customer.transparent_redirect_update_url())
-        result = Customer.confirm_transparent_redirect(query_string)
-        self.assertTrue(result.is_success)
-        customer = result.customer
-        self.assertEqual("John", customer.first_name)
-        self.assertEqual("john@doe.com", customer.email)
-
-    def test_update_with_nested_values_via_transparent_redirect(self):
-        customer = Customer.create({
-            "first_name": "Joe",
-            "last_name": "Brown",
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "10/10",
-                "billing_address": {
-                    "postal_code": "11111"
-                }
-            }
-        }).customer
-        credit_card = customer.credit_cards[0]
-        address = credit_card.billing_address
-
-        tr_data = {
-            "customer_id": customer.id,
-            "customer": {
-                "first_name": "Joe",
-                "last_name": "Brown",
-                "credit_card": {
-                    "expiration_date": "12/12",
-                    "options": {
-                        "update_existing_token": credit_card.token
-                    },
-                    "billing_address": {
-                        "postal_code": "44444",
-                        "options": {
-                            "update_existing": True
-                        }
-                    }
-                }
-            }
-        }
-        post_params = {
-            "tr_data": Customer.tr_data_for_update(tr_data, "http://example.com/path"),
-        }
-
-        query_string = TestHelper.simulate_tr_form_post(post_params, Customer.transparent_redirect_update_url())
-        updated_customer = Customer.confirm_transparent_redirect(query_string).customer
-        updated_credit_card = CreditCard.find(credit_card.token)
-        updated_address = Address.find(customer.id, address.id)
-
-        self.assertEqual("Joe", updated_customer.first_name)
-        self.assertEqual("Brown", updated_customer.last_name)
-        self.assertEqual("12/2012", updated_credit_card.expiration_date)
-        self.assertEqual("44444", updated_address.postal_code)
-
-    def test_update_from_transparent_redirect_with_error_result(self):
-        customer = Customer.create({
-            "first_name": "Jane",
-        }).customer
-
-        tr_data = {
-            "customer_id": customer.id,
-            "customer": {
-                "first_name": "John",
-            }
-        }
-        post_params = {
-            "tr_data": Customer.tr_data_for_update(tr_data, "http://example.com/path"),
-            "customer[email]": "john#doe.com",
-        }
-
-        query_string = TestHelper.simulate_tr_form_post(post_params, Customer.transparent_redirect_update_url())
-        result = Customer.confirm_transparent_redirect(query_string)
-        self.assertFalse(result.is_success)
-
-        customer_email_errors = result.errors.for_object("customer").on("email")
-        self.assertEqual(1, len(customer_email_errors))
-        self.assertEqual(ErrorCodes.Customer.EmailIsInvalid, customer_email_errors[0].code)
-
-    def test_customer_payment_methods(self):
-        customer = Customer("gateway", {
-            "credit_cards": [{"token": "credit_card"}],
-            "paypal_accounts": [{"token": "paypal_account"}],
-            "apple_pay_cards": [{"token": "apple_pay_card"}],
-            "android_pay_cards": [{"token": "android_pay_card"}],
-            "us_bank_accounts": [{"token": "us_bank_account"}]
-            })
-
-        payment_method_tokens = [ pm.token for pm in customer.payment_methods ]
-
-        self.assertEqual(sorted(payment_method_tokens), ["android_pay_card", "apple_pay_card", "credit_card", "paypal_account", "us_bank_account"])
diff --git a/tests/integration/test_customer_search.py b/tests/integration/test_customer_search.py
deleted file mode 100644
index e505d14..0000000
--- a/tests/integration/test_customer_search.py
+++ /dev/null
@@ -1,151 +0,0 @@
-from tests.test_helper import *
-
-class TestCustomerSearch(unittest.TestCase):
-    def test_advanced_search_no_results(self):
-        collection = Transaction.search([
-            TransactionSearch.billing_first_name == "no_such_person"
-        ])
-        self.assertEqual(0, collection.maximum_size)
-
-    def test_advanced_search_finds_duplicate_cards_given_payment_method_token(self):
-        credit_card_dict = {
-            "number": "63049580000009",
-            "expiration_date": "05/2010"
-        }
-
-        jim_dict = {
-            "first_name": "Jim",
-            "credit_card": credit_card_dict
-        }
-
-        joe_dict = {
-            "first_name": "Joe",
-            "credit_card": credit_card_dict
-        }
-
-        jim = Customer.create(jim_dict).customer
-        joe = Customer.create(joe_dict).customer
-
-        collection = Customer.search(
-            CustomerSearch.payment_method_token_with_duplicates == jim.credit_cards[0].token,
-        )
-
-        customer_ids = [customer.id for customer in collection.items]
-        self.assertTrue(jim.id in customer_ids)
-        self.assertTrue(joe.id in customer_ids)
-
-
-    def test_advanced_search_searches_all_text_fields(self):
-        token = "creditcard%s" % random.randint(1, 100000)
-
-        customer = Customer.create({
-            "first_name": "Timmy",
-            "last_name": "O'Toole",
-            "company": "O'Toole and Son(s)",
-            "email": "timmy@example.com",
-            "fax": "3145551234",
-            "phone": "5551231234",
-            "website": "http://example.com",
-            "credit_card": {
-                "cardholder_name": "Tim Toole",
-                "number": "4111111111111111",
-                "expiration_date": "05/2010",
-                "token": token,
-                "billing_address": {
-                    "first_name": "Thomas",
-                    "last_name": "Otool",
-                    "street_address": "1 E Main St",
-                    "extended_address": "Suite 3",
-                    "locality": "Chicago",
-                    "region": "Illinois",
-                    "postal_code": "60622",
-                    "country_name": "United States of America"
-                }
-            }
-        }).customer
-
-        search_criteria = {
-            "first_name": "Timmy",
-            "last_name": "O'Toole",
-            "company": "O'Toole and Son(s)",
-            "email": "timmy@example.com",
-            "phone": "5551231234",
-            "fax": "3145551234",
-            "website": "http://example.com",
-            "address_first_name": "Thomas",
-            "address_last_name": "Otool",
-            "address_street_address": "1 E Main St",
-            "address_postal_code": "60622",
-            "address_extended_address": "Suite 3",
-            "address_locality": "Chicago",
-            "address_region": "Illinois",
-            "address_country_name": "United States of America",
-            "payment_method_token": token,
-            "cardholder_name": "Tim Toole",
-            "credit_card_number": "4111111111111111",
-            "credit_card_expiration_date": "05/2010"
-        }
-
-        criteria = [getattr(CustomerSearch, search_field) == value for search_field, value in search_criteria.items()]
-        criteria.append(CustomerSearch.id == customer.id)
-
-        collection = Customer.search(criteria)
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(customer.id, collection.first.id)
-
-        for search_field, value in search_criteria.items():
-            collection = Customer.search(
-                CustomerSearch.id == customer.id,
-                getattr(CustomerSearch, search_field) == value
-            )
-
-            self.assertEqual(1, collection.maximum_size)
-            self.assertEqual(customer.id, collection.first.id)
-
-    def test_advanced_search_range_node_created_at(self):
-        customer = Customer.create().customer
-
-        past = customer.created_at - timedelta(minutes=10)
-        future = customer.created_at + timedelta(minutes=10)
-
-        collection = Customer.search(
-            CustomerSearch.id == customer.id,
-            CustomerSearch.created_at.between(past, future)
-        )
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(customer.id, collection.first.id)
-
-        collection = Customer.search(
-            CustomerSearch.id == customer.id,
-            CustomerSearch.created_at <= future
-        )
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(customer.id, collection.first.id)
-
-        collection = Customer.search(
-            CustomerSearch.id == customer.id,
-            CustomerSearch.created_at >= past
-        )
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(customer.id, collection.first.id)
-
-    def test_search_on_paypal_account_email(self):
-        http = ClientApiHttp.create()
-        status_code, nonce = http.get_paypal_nonce({
-            "consent-code": "consent-code",
-            "options": {"validate": False}
-        })
-        self.assertEqual(202, status_code)
-
-        customer = Customer.create({"payment_method_nonce": nonce}).customer
-
-        collection = Customer.search(
-            CustomerSearch.paypal_account_email == "jane.doe@example.com",
-            CustomerSearch.id == customer.id
-        )
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(customer.id, collection.first.id)
diff --git a/tests/integration/test_disbursement.py b/tests/integration/test_disbursement.py
deleted file mode 100644
index bbe4280..0000000
--- a/tests/integration/test_disbursement.py
+++ /dev/null
@@ -1,25 +0,0 @@
-from tests.test_helper import *
-from datetime import date
-
-class TestDisbursement(unittest.TestCase):
-    def test_disbursement_finds_transactions(self):
-        disbursement = Disbursement(Configuration.gateway(), {
-            "merchant_account": {
-                "id": "sub_merchant_account",
-                "status": "active",
-                "master_merchant_account": {
-                    "id": "master_merchant_account",
-                    "status": "active"
-                },
-            },
-            "id": "123456",
-            "exception_message": "invalid_account_number",
-            "amount": "100.00",
-            "disbursement_date": date(2013, 4, 10),
-            "follow_up_action": "update",
-            "transaction_ids": ["sub_merchant_transaction"]
-        })
-
-        transactions = disbursement.transactions()
-        self.assertEqual(1, transactions.maximum_size)
-        self.assertEqual("sub_merchant_transaction", transactions.first.id)
diff --git a/tests/integration/test_discounts.py b/tests/integration/test_discounts.py
deleted file mode 100644
index 981677e..0000000
--- a/tests/integration/test_discounts.py
+++ /dev/null
@@ -1,35 +0,0 @@
-from tests.test_helper import *
-
-class TestDiscounts(unittest.TestCase):
-
-    def test_all_returns_all_discounts(self):
-        new_id = str(random.randint(1, 1000000))
-        attributes = {
-            "amount": "100.00",
-            "description": "some description",
-            "id": new_id,
-            "kind": "discount",
-            "name": "python_discount",
-            "never_expires": False,
-            "number_of_billing_cycles": 1
-        }
-
-        Configuration.instantiate().http().post(Configuration.instantiate().base_merchant_path() + "/modifications/create_modification_for_tests", {"modification": attributes})
-
-        discounts = Discount.all()
-
-        for discount in discounts:
-            if discount.id == new_id:
-                break
-        else:
-            discount = None
-
-        self.assertNotEqual(None, discount)
-
-        self.assertEqual(Decimal("100.00"), discount.amount)
-        self.assertEqual("some description", discount.description)
-        self.assertEqual(new_id, discount.id)
-        self.assertEqual("discount", discount.kind)
-        self.assertEqual("python_discount", discount.name)
-        self.assertEqual(False, discount.never_expires)
-        self.assertEqual(1, discount.number_of_billing_cycles)
diff --git a/tests/integration/test_dispute_search.py b/tests/integration/test_dispute_search.py
deleted file mode 100644
index 63d13f4..0000000
--- a/tests/integration/test_dispute_search.py
+++ /dev/null
@@ -1,127 +0,0 @@
-from tests.test_helper import *
-from braintree.test.credit_card_numbers import CreditCardNumbers
-
-class TestDisputeSearch(unittest.TestCase):
-    def create_sample_disputed_transaction(self):
-        customer = Customer.create({
-            "first_name": "Jen",
-            "last_name": "Smith",
-            "company": "Braintree",
-            "email": "jen@example.com",
-            "phone": "312.555.1234",
-            "fax": "614.555.5678",
-            "website": "www.example.com",
-        }).customer
-
-        return Transaction.sale({
-            "amount": "100.00",
-            "credit_card": {
-                "number": CreditCardNumbers.Disputes.Chargeback,
-                "expiration_date": "12/2019",
-            },
-            "customer_id": customer.id,
-            "merchant_account_id": "14LaddersLLC_instant",
-            "options": {
-                "submit_for_settlement": True,
-            },
-        }).transaction
-
-    def test_advanced_search_no_results(self):
-        collection = Dispute.search([
-            DisputeSearch.id == "non_existent_dispute"
-        ])
-
-        disputes = [dispute for dispute in collection.disputes.items]
-        self.assertEquals(0, len(disputes))
-
-    def test_advanced_search_returns_single_dispute_by_customer_id(self):
-        transaction = self.create_sample_disputed_transaction()
-
-        collection = Dispute.search([
-            DisputeSearch.customer_id == transaction.customer_details.id
-        ])
-
-        disputes = [dispute for dispute in collection.disputes.items]
-        self.assertEquals(1, len(disputes))
-
-        dispute = disputes[0]
-
-        self.assertEquals(dispute.id, transaction.disputes[0].id)
-        self.assertEquals(dispute.status, Dispute.Status.Open)
-
-    def test_advanced_search_returns_single_dispute_by_id(self):
-        collection = Dispute.search([
-            DisputeSearch.id == "open_dispute"
-        ])
-
-        disputes = [dispute for dispute in collection.disputes.items]
-        self.assertEquals(1, len(disputes))
-
-        dispute = disputes[0]
-
-        self.assertEquals(dispute.id, "open_dispute")
-        self.assertEquals(dispute.status, Dispute.Status.Open)
-
-    def test_advanced_search_returns_disputes_by_multiple_reasons(self):
-        collection = Dispute.search([
-            DisputeSearch.reason.in_list([
-                braintree.Dispute.Reason.ProductUnsatisfactory,
-                braintree.Dispute.Reason.Retrieval
-            ])
-        ])
-
-        disputes = [dispute for dispute in collection.disputes.items]
-        self.assertEquals(2, len(disputes))
-
-    def test_advanced_search_returns_disputes_by_date_range(self):
-        collection = Dispute.search([
-            DisputeSearch.received_date.between("03/03/2014", "03/05/2014")
-        ])
-
-        disputes = [dispute for dispute in collection.disputes.items]
-        self.assertEquals(1, len(disputes))
-
-        self.assertEquals(disputes[0].received_date, date(2014, 3, 4))
-
-    def test_advanced_search_returns_disputes_by_disbursement_date_range(self):
-        transaction = self.create_sample_disputed_transaction()
-        disbursement_date = transaction.disputes[0].status_history[0].disbursement_date
-
-        collection = Dispute.search([
-            DisputeSearch.disbursement_date.between(disbursement_date, disbursement_date)
-        ])
-
-        disputes = [dispute for dispute in collection.disputes.items]
-        self.assertGreaterEqual(len(disputes), 1)
-
-        self.assertEquals(disputes[0].status_history[0].disbursement_date, disbursement_date)
-
-    def test_advanced_search_returns_disputes_by_effective_date_range(self):
-        transaction = self.create_sample_disputed_transaction()
-        effective_date = transaction.disputes[0].status_history[0].effective_date
-
-        collection = Dispute.search([
-            DisputeSearch.effective_date.between(effective_date, effective_date)
-        ])
-
-        disputes = [dispute for dispute in collection.disputes.items]
-        self.assertGreaterEqual(len(disputes), 1)
-
-        self.assertEquals(disputes[0].status_history[0].effective_date, effective_date)
-
-    def test_advanced_search_returns_disputes_by_amount_and_status(self):
-        collection = Dispute.search([
-            DisputeSearch.amount_disputed.between("1.00", "100.00"),
-            DisputeSearch.id == "open_dispute"
-        ])
-
-        disputes = [dispute for dispute in collection.disputes.items]
-        self.assertEquals(1, len(disputes))
-
-    def test_advanced_search_can_take_one_criteria(self):
-        collection = Dispute.search(
-            DisputeSearch.id == "non_existent_dispute"
-        )
-
-        disputes = [dispute for dispute in collection.disputes.items]
-        self.assertEquals(0, len(disputes))
diff --git a/tests/integration/test_disputes.py b/tests/integration/test_disputes.py
deleted file mode 100644
index 8f11a6b..0000000
--- a/tests/integration/test_disputes.py
+++ /dev/null
@@ -1,295 +0,0 @@
-import re
-import time
-import datetime
-from tests.test_helper import *
-from braintree.test.credit_card_numbers import CreditCardNumbers
-
-class TestDisputes(unittest.TestCase):
-    def create_evidence_document(self):
-        file_path = os.path.join(os.path.dirname(__file__), "..", "fixtures/bt_logo.png")
-        png_file = open(file_path, "rb")
-
-        return DocumentUpload.create({
-            "kind": braintree.DocumentUpload.Kind.EvidenceDocument,
-            "file": png_file
-        }).document_upload
-
-    def create_sample_dispute(self):
-        return Transaction.sale({
-            "amount": "100.00",
-            "credit_card": {
-                "number": CreditCardNumbers.Disputes.Chargeback,
-                "expiration_date": "12/2019"
-            }
-        }).transaction.disputes[0]
-
-    def test_accept_changes_dispute_status_to_accepted(self):
-        dispute = self.create_sample_dispute()
-
-        result = Dispute.accept(dispute.id)
-
-        self.assertTrue(result.is_success)
-
-        updated_dispute = Dispute.find(dispute.id)
-
-        self.assertEqual(updated_dispute.status, Dispute.Status.Accepted)
-
-        dispute_from_transaction = Transaction.find(dispute.transaction.id).disputes[0]
-
-        self.assertEqual(dispute_from_transaction.status, Dispute.Status.Accepted)
-
-    def test_accept_errors_when_dispute_not_open(self):
-        result = Dispute.accept("wells_dispute")
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(result.errors.for_object("dispute")[0].code, ErrorCodes.Dispute.CanOnlyAcceptOpenDispute)
-        self.assertEqual(result.errors.for_object("dispute")[0].message, "Disputes can only be accepted when they are in an Open state")
-
-    @raises_with_regexp(NotFoundError, "dispute with id 'invalid-id' not found")
-    def test_accept_raises_error_when_dispute_not_found(self):
-        dispute = Dispute.accept("invalid-id")
-
-    def test_add_file_evidence_adds_evidence(self):
-        dispute = self.create_sample_dispute()
-        document = self.create_evidence_document()
-
-        result = Dispute.add_file_evidence(dispute.id, document.id)
-
-        self.assertTrue(result.is_success)
-
-        updated_dispute = Dispute.find(dispute.id)
-
-        self.assertEqual(updated_dispute.evidence[0].id, result.evidence.id)
-
-    def test_add_file_evidence_adds_category_file_evidence(self):
-        dispute = self.create_sample_dispute()
-        document = self.create_evidence_document()
-
-        result = Dispute.add_file_evidence(dispute.id, { "document_id": document.id, "category": "GENERAL" })
-
-        self.assertTrue(result.is_success)
-        self.assertEqual(result.evidence.category, "GENERAL")
-
-    @raises_with_regexp(NotFoundError, "dispute with id 'unknown_dispute_id' not found")
-    def test_add_file_evidence_raises_error_when_dispute_not_found(self):
-        dispute = Dispute.add_file_evidence("unknown_dispute_id", "text evidence")
-
-    def test_add_file_evidence_raises_error_when_dispute_not_open(self):
-        dispute = self.create_sample_dispute()
-        document = self.create_evidence_document()
-
-        Dispute.accept(dispute.id)
-
-        result = Dispute.add_file_evidence(dispute.id, document.id)
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(result.errors.for_object("dispute")[0].code, ErrorCodes.Dispute.CanOnlyAddEvidenceToOpenDispute)
-        self.assertEqual(result.errors.for_object("dispute")[0].message, "Evidence can only be attached to disputes that are in an Open state")
-
-    def test_categorized_file_evidence_for_text_only_category(self):
-        dispute = self.create_sample_dispute()
-        document = self.create_evidence_document()
-
-        result = Dispute.add_file_evidence(dispute.id, { "document_id": document.id, "category": "DEVICE_ID" })
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(result.errors.for_object("dispute")[0].code, ErrorCodes.Dispute.EvidenceCategoryTextOnly)
-        self.assertEqual(result.errors.for_object("dispute")[0].message, "Only text evidence can be provided for this category")
-
-    def test_categorized_file_evidence_with_unsupported_category(self):
-        dispute = self.create_sample_dispute()
-        document = self.create_evidence_document()
-
-        result = Dispute.add_file_evidence(dispute.id, { "document_id": document.id, "category": "DOESNOTEXIST" })
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(result.errors.for_object("dispute")[0].code, ErrorCodes.Dispute.CanOnlyCreateEvidenceWithValidCategory)
-        self.assertEqual(result.errors.for_object("dispute")[0].message, "The category you supplied on the evidence record is not valid")
-
-    def test_add_text_evidence_adds_text_evidence(self):
-        dispute = self.create_sample_dispute()
-
-        result = Dispute.add_text_evidence(dispute.id, "text evidence")
-        evidence = result.evidence
-
-        self.assertTrue(result.is_success)
-        self.assertEqual(evidence.comment, "text evidence")
-        self.assertIsNotNone(evidence.created_at)
-        self.assertTrue(re.match("^\w{16,}$", evidence.id))
-        self.assertIsNone(evidence.sent_to_processor_at)
-        self.assertIsNone(evidence.url)
-        self.assertIsNone(evidence.category)
-        self.assertIsNone(evidence.sequence_number)
-
-    def test_add_text_evidence_adds_tag_and_sequence_number_text_evidence(self):
-        dispute = self.create_sample_dispute()
-
-        result = Dispute.add_text_evidence(dispute.id, { "content": "PROOF_OF_FULFILLMENT", "tag": "EVIDENCE_TYPE" })
-        result_carrier_name = Dispute.add_text_evidence(dispute.id, { "content": "UPS", "tag": "CARRIER_NAME", "sequence_number": "0" })
-        result_tracking_number = Dispute.add_text_evidence(dispute.id, { "content": "UPS-1243", "tag": "TRACKING_NUMBER", "sequence_number": "0" })
-
-        self.assertTrue(result.is_success)
-        evidence = result.evidence
-        self.assertEqual(evidence.comment, "PROOF_OF_FULFILLMENT")
-        self.assertEqual(evidence.tag, "EVIDENCE_TYPE")
-        self.assertIsNone(evidence.sequence_number)
-
-        self.assertTrue(result_carrier_name.is_success)
-        evidence = result_carrier_name.evidence
-        self.assertEqual(evidence.comment, "UPS")
-        self.assertEqual(evidence.tag, "CARRIER_NAME")
-        self.assertEqual(evidence.sequence_number, 0)
-
-        self.assertTrue(result_tracking_number.is_success)
-        evidence = result_tracking_number.evidence
-        self.assertEqual(evidence.comment, "UPS-1243")
-        self.assertEqual(evidence.tag, "TRACKING_NUMBER")
-        self.assertEqual(evidence.sequence_number, 0)
-
-    def test_add_text_evidence_adds_category_text_evidence(self):
-        dispute = self.create_sample_dispute()
-
-        result = Dispute.add_text_evidence(dispute.id, { "content": "device id" , "category": "DEVICE_ID" })
-
-        self.assertTrue(result.is_success)
-        evidence = result.evidence
-        self.assertEqual(evidence.comment, "device id")
-        self.assertEqual(evidence.category, "DEVICE_ID")
-
-    @raises_with_regexp(NotFoundError, "Dispute with ID 'unknown_dispute_id' not found")
-    def test_add_text_evidence_raises_error_when_dispute_not_found(self):
-        dispute = Dispute.add_text_evidence("unknown_dispute_id", "text evidence")
-
-    def test_add_text_evidence_raises_error_when_dispute_not_open(self):
-        dispute = self.create_sample_dispute()
-
-        Dispute.accept(dispute.id)
-        result = Dispute.add_text_evidence(dispute.id, "text evidence")
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(result.errors.for_object("dispute")[0].code, ErrorCodes.Dispute.CanOnlyAddEvidenceToOpenDispute)
-        self.assertEqual(result.errors.for_object("dispute")[0].message, "Evidence can only be attached to disputes that are in an Open state")
-
-    def test_add_text_evidence_shows_new_record_in_find(self):
-        dispute = self.create_sample_dispute()
-
-        evidence = Dispute.add_text_evidence(dispute.id, "text evidence").evidence
-
-        refreshed_dispute = Dispute.find(dispute.id)
-
-        self.assertEqual(refreshed_dispute.evidence[0].id, evidence.id)
-        self.assertEqual(refreshed_dispute.evidence[0].comment, "text evidence")
-
-    def test_categorized_text_evidence_with_unsupported_category(self):
-        dispute = self.create_sample_dispute()
-        result = Dispute.add_text_evidence(dispute.id, { "content": "evidence", "category": "DOESNOTEXIST" })
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(result.errors.for_object("dispute")[0].code, ErrorCodes.Dispute.CanOnlyCreateEvidenceWithValidCategory)
-        self.assertEqual(result.errors.for_object("dispute")[0].message, "The category you supplied on the evidence record is not valid")
-
-    def test_categorized_text_evidence_with_file_category(self):
-        dispute = self.create_sample_dispute()
-        result = Dispute.add_text_evidence(dispute.id, { "content": "evidence", "category": "MERCHANT_WEBSITE_OR_APP_ACCESS" })
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(result.errors.for_object("dispute")[0].code, ErrorCodes.Dispute.EvidenceCategoryDocumentOnly)
-        self.assertEqual(result.errors.for_object("dispute")[0].message, "Only document evidence can be provided for this category")
-
-    def test_categorized_text_evidence_with_invalid_date_time_format(self):
-        dispute = self.create_sample_dispute()
-        result = Dispute.add_text_evidence(dispute.id, { "content": "not a date", "category": "DOWNLOAD_DATE_TIME" })
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(result.errors.for_object("dispute")[0].code, ErrorCodes.Dispute.EvidenceContentDateInvalid)
-
-    def test_categorized_text_evidence_with_valid_date_time_format(self):
-        dispute = self.create_sample_dispute()
-        result = Dispute.add_text_evidence(dispute.id, { "content": "2018-10-20T18:00:00-0500", "category": "DOWNLOAD_DATE_TIME" })
-
-        self.assertTrue(result.is_success)
-
-    def test_finalize_changes_dispute_status_to_disputed(self):
-        dispute = self.create_sample_dispute()
-
-        result = Dispute.finalize(dispute.id)
-
-        self.assertTrue(result.is_success)
-
-        updated_dispute = Dispute.find(dispute.id)
-
-        self.assertEqual(updated_dispute.status, Dispute.Status.Disputed)
-
-    def test_finalize_errors_when_dispute_not_open(self):
-        result = Dispute.finalize("wells_dispute")
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(result.errors.for_object("dispute")[0].code, ErrorCodes.Dispute.CanOnlyFinalizeOpenDispute)
-        self.assertEqual(result.errors.for_object("dispute")[0].message, "Disputes can only be finalized when they are in an Open state")
-
-    def test_finalize_when_digital_goods_missing(self):
-        dispute = self.create_sample_dispute()
-        result = Dispute.add_text_evidence(dispute.id, { "content": "device_id", "category": "DEVICE_ID" })
-
-        self.assertTrue(result.is_success)
-
-        result = dispute.finalize(dispute.id)
-
-        self.assertFalse(result.is_success)
-
-        error_codes = [error.code for error in result.errors.for_object("dispute")]
-        self.assertIn(ErrorCodes.Dispute.DigitalGoodsMissingDownloadDate, error_codes)
-        self.assertIn(ErrorCodes.Dispute.DigitalGoodsMissingEvidence, error_codes)
-
-    def test_finalize_when_missing_non_disputed_payments_date(self):
-        dispute = self.create_sample_dispute()
-        result = Dispute.add_text_evidence(dispute.id, { "content": "123", "category": "PRIOR_NON_DISPUTED_TRANSACTION_ARN" })
-
-        self.assertTrue(result.is_success)
-
-        result = dispute.finalize(dispute.id)
-
-        self.assertFalse(result.is_success)
-
-        error_codes = [error.code for error in result.errors.for_object("dispute")]
-        self.assertIn(ErrorCodes.Dispute.NonDisputedPriorTransactionEvidenceMissingDate, error_codes)
-
-    @raises_with_regexp(NotFoundError, "dispute with id 'invalid-id' not found")
-    def test_finalize_raises_error_when_dispute_not_found(self):
-        dispute = Dispute.finalize("invalid-id")
-
-    def test_find_returns_dispute_with_given_id(self):
-        dispute = Dispute.find("open_dispute")
-
-        self.assertEqual(dispute.amount_disputed, 31.0)
-        self.assertEqual(dispute.amount_won, 0.0)
-        self.assertEqual(dispute.id, "open_dispute")
-        self.assertEqual(dispute.status, Dispute.Status.Open)
-        self.assertEqual(dispute.transaction.id, "open_disputed_transaction")
-
-    @raises_with_regexp(NotFoundError, "dispute with id 'invalid-id' not found")
-    def test_find_raises_error_when_dispute_not_found(self):
-        dispute = Dispute.find("invalid-id")
-
-    def test_remove_evidence_removes_evidence_from_the_dispute(self):
-        dispute = self.create_sample_dispute()
-        evidence = Dispute.add_text_evidence(dispute.id, "text evidence").evidence
-        result = Dispute.remove_evidence(dispute.id, evidence.id)
-
-        self.assertTrue(result.is_success)
-
-    @raises_with_regexp(NotFoundError, "evidence with id 'unknown_evidence_id' for dispute with id 'unknown_dispute_id' not found")
-    def test_remove_evidence_raises_error_when_dispute_or_evidence_not_found(self):
-        Dispute.remove_evidence("unknown_dispute_id", "unknown_evidence_id")
-
-    def test_remove_evidence_errors_when_dispute_not_open(self):
-        dispute = self.create_sample_dispute()
-        evidence = Dispute.add_text_evidence(dispute.id, "text evidence").evidence
-
-        Dispute.accept(dispute.id)
-
-        result = Dispute.remove_evidence(dispute.id, evidence.id)
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(result.errors.for_object("dispute")[0].code, ErrorCodes.Dispute.CanOnlyRemoveEvidenceFromOpenDispute)
-        self.assertEqual(result.errors.for_object("dispute")[0].message, "Evidence can only be removed from disputes that are in an Open state")
diff --git a/tests/integration/test_document_upload.py b/tests/integration/test_document_upload.py
deleted file mode 100644
index 773a62b..0000000
--- a/tests/integration/test_document_upload.py
+++ /dev/null
@@ -1,103 +0,0 @@
-import os
-from nose.exc import SkipTest
-from tests.test_helper import *
-from braintree.test.nonces import Nonces
-
-class TestDocumentUpload(unittest.TestCase):
-    def setUp(self):
-        file_path = os.path.join(os.path.dirname(__file__), "..", "fixtures/bt_logo.png")
-        self.png_file = open(file_path, "rb")
-
-    def test_create_returns_successful_result_if_valid(self):
-        result = DocumentUpload.create({
-            "kind": braintree.DocumentUpload.Kind.EvidenceDocument,
-            "file": self.png_file
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertTrue(result.document_upload.id != None)
-        self.assertEqual(result.document_upload.content_type, "image/png")
-        self.assertEqual(result.document_upload.kind, braintree.DocumentUpload.Kind.EvidenceDocument)
-        self.assertEqual(result.document_upload.name, "bt_logo.png")
-        self.assertEqual(result.document_upload.size, 2443)
-
-    def test_create_returns_error_with_unsupported_file_type(self):
-        file_path = os.path.join(os.path.dirname(__file__), "..", "fixtures/gif_extension_bt_logo.gif")
-        gif_file = open(file_path, "rb")
-
-        result = DocumentUpload.create({
-            "kind": braintree.DocumentUpload.Kind.EvidenceDocument,
-            "file": gif_file
-        })
-
-        self.assertEqual(result.errors.for_object("document_upload")[0].code, ErrorCodes.DocumentUpload.FileTypeIsInvalid)
-
-    def test_create_returns_error_with_malformed_file(self):
-        file_path = os.path.join(os.path.dirname(__file__), "..", "fixtures/malformed_pdf.pdf")
-        bad_pdf_file = open(file_path, "rb")
-
-        result = DocumentUpload.create({
-            "kind": braintree.DocumentUpload.Kind.EvidenceDocument,
-            "file": bad_pdf_file
-        })
-
-        self.assertEqual(result.errors.for_object("document_upload")[0].code, ErrorCodes.DocumentUpload.FileIsMalformedOrEncrypted)
-
-    def test_create_returns_error_with_invalid_kind(self):
-        result = DocumentUpload.create({
-            "kind": "invalid_kind",
-            "file": self.png_file
-        })
-
-        self.assertEqual(result.errors.for_object("document_upload")[0].code, ErrorCodes.DocumentUpload.KindIsInvalid)
-
-    def test_create_returns_error_when_file_is_over_4mb(self):
-        file_path = os.path.join(os.path.dirname(__file__), "..", "fixtures/large_file.png")
-        try:
-            f = open(file_path, 'w+')
-            for i in range(1048577 * 4):
-                f.write('a')
-            f.close()
-
-            large_file = open(file_path, 'rb')
-
-            result = DocumentUpload.create({
-                "kind": braintree.DocumentUpload.Kind.EvidenceDocument,
-                "file": large_file
-            })
-
-            self.assertEqual(result.errors.for_object("document_upload")[0].code, ErrorCodes.DocumentUpload.FileIsTooLarge)
-        finally:
-            os.remove(file_path)
-
-    def test_create_returns_error_with_malformed_file(self):
-        file_path = os.path.join(os.path.dirname(__file__), "..", "fixtures/too_long.pdf")
-        too_long_pdf = open(file_path, "rb")
-
-        result = DocumentUpload.create({
-            "kind": braintree.DocumentUpload.Kind.EvidenceDocument,
-            "file": too_long_pdf
-        })
-
-        self.assertEqual(result.errors.for_object("document_upload")[0].code, ErrorCodes.DocumentUpload.FileIsTooLong)
-
-    @raises_with_regexp(KeyError, "'Invalid keys: invalid_key'")
-    def test_create_returns_invalid_keys_errors_with_invalid_signature(self):
-        result = DocumentUpload.create({
-            "kind": braintree.DocumentUpload.Kind.EvidenceDocument,
-            "invalid_key": "do not add"
-        })
-
-    @raises_with_regexp(ValueError, "file must be a file handle")
-    def test_create_throws_error_when_not_valid_file(self):
-        result = DocumentUpload.create({
-            "kind": braintree.DocumentUpload.Kind.EvidenceDocument,
-            "file": "not_a_file"
-        })
-
-    @raises_with_regexp(ValueError, "file must be a file handle")
-    def test_create_throws_error_when_none_file(self):
-        result = DocumentUpload.create({
-            "kind": braintree.DocumentUpload.Kind.EvidenceDocument,
-            "file": None
-        })
diff --git a/tests/integration/test_graphql_client.py b/tests/integration/test_graphql_client.py
deleted file mode 100644
index 8e4d61f..0000000
--- a/tests/integration/test_graphql_client.py
+++ /dev/null
@@ -1,52 +0,0 @@
-from unittest import TestCase
-
-from braintree.configuration import Configuration
-from braintree.environment import Environment
-
-class TestGraphQLClient(TestCase):
-
-    @staticmethod
-    def get_graphql_client(environment):
-        config = Configuration(environment, "integration_merchant_id",
-                               public_key="integration_public_key",
-                               private_key="integration_private_key")
-        return config.graphql_client()
-
-    def test_graphql_makes_valid_queries_without_variables(self):
-        definition = '''
-          query {
-            ping
-          }
-        '''
-        graphql_client = self.get_graphql_client(Environment.Development)
-        response = graphql_client.query(definition)
-
-        self.assertTrue("data" in response)
-        self.assertTrue("ping" in response["data"])
-        self.assertTrue("pong" == response["data"]["ping"])
-
-    def test_graphql_makes_valid_queries_with_variables(self):
-        definition = '''
-          mutation CreateClientToken($input: CreateClientTokenInput!) {
-            createClientToken(input: $input) {
-            clientMutationId
-            clientToken
-            }
-          }
-        '''
-
-        variables = {
-            "input": {
-                "clientMutationId": "abc123",
-                "clientToken": {
-                    "merchantAccountId": "ABC123"
-                }
-            }
-        }
-
-        graphql_client = self.get_graphql_client(Environment.Development)
-        response = graphql_client.query(definition, variables)
-
-        self.assertTrue("data" in response)
-        self.assertTrue("createClientToken" in response["data"])
-        self.assertTrue("clientToken" in response["data"]["createClientToken"])
diff --git a/tests/integration/test_http.py b/tests/integration/test_http.py
deleted file mode 100644
index b87218a..0000000
--- a/tests/integration/test_http.py
+++ /dev/null
@@ -1,94 +0,0 @@
-from tests.test_helper import *
-from distutils.version import LooseVersion
-import platform
-import braintree
-import requests
-
-class TestHttp(unittest.TestCase):
-    if LooseVersion(requests.__version__) >= LooseVersion('1.0.0'):
-        SSLError = requests.exceptions.SSLError
-    else:
-        SSLError = requests.models.SSLError
-
-    @staticmethod
-    def get_http(environment):
-        config = Configuration(environment, "merchant_id", public_key="public_key", private_key="private_key")
-        return config.http()
-
-    @raises(AuthenticationError)
-    def test_successful_connection_sandbox(self):
-        http = self.get_http(Environment.Sandbox)
-        http.get("/")
-
-    @raises(AuthenticationError)
-    def test_successful_connection_production(self):
-        http = self.get_http(Environment.Production)
-        http.get("/")
-
-    def test_wrapping_http_exceptions(self):
-        config = Configuration(
-            Environment("test", "localhost", "1", False, None, Environment.Production.ssl_certificate),
-            "integration_merchant_id",
-            public_key="integration_public_key",
-            private_key="integration_private_key",
-            wrap_http_exceptions=True
-        )
-
-        gateway = braintree.braintree_gateway.BraintreeGateway(config)
-
-        try:
-            gateway.transaction.find("my_id")
-        except braintree.exceptions.unexpected_error.UnexpectedError:
-            correct_exception = True
-        except Exception:
-            correct_exception = False
-
-        self.assertTrue(correct_exception)
-
-    def test_unsuccessful_connection_to_good_ssl_server_with_wrong_cert(self):
-        if platform.system() == "Darwin":
-            return
-
-        #any endpoint that returns valid XML with a status of 3xx or less and serves SSL
-        environment = Environment("test", "aws.amazon.com/ec2", "443", "http://auth.venmo.dev:9292", True, Environment.Production.ssl_certificate)
-        http = self.get_http(environment)
-        try:
-            http.get("/")
-        except self.SSLError as e:
-            self.assertTrue("certificate verify failed" in str(e))
-        except AuthenticationError:
-            self.fail("Expected to receive an SSL error but received an Authentication Error instead, check your local openssl installation")
-        else:
-            self.fail("Expected to receive an SSL error but no exception was raised")
-
-    def test_unsuccessful_connection_to_ssl_server_with_wrong_domain(self):
-        #ip address of api.braintreegateway.com
-        environment = Environment("test", "204.109.13.121", "443", "http://auth.venmo.dev:9292", True, Environment.Production.ssl_certificate)
-        http = self.get_http(environment)
-        try:
-            http.get("/")
-        except self.SSLError:
-            pass
-        else:
-            self.fail("Expected to receive an SSL error but no exception was raised")
-
-    def test_timeouts(self):
-        config = Configuration(
-            Environment.Development,
-            "integration_merchant_id",
-            public_key="integration_public_key",
-            private_key="integration_private_key",
-            wrap_http_exceptions=True,
-            timeout=0.001
-        )
-
-        gateway = braintree.braintree_gateway.BraintreeGateway(config)
-
-        try:
-            gateway.transaction.find("my_id")
-        except braintree.exceptions.http.timeout_error.TimeoutError:
-            correct_exception = True
-        except Exception:
-            correct_exception = False
-
-        self.assertTrue(correct_exception)
diff --git a/tests/integration/test_masterpass.py b/tests/integration/test_masterpass.py
deleted file mode 100644
index 1d3990e..0000000
--- a/tests/integration/test_masterpass.py
+++ /dev/null
@@ -1,95 +0,0 @@
-from tests.test_helper import *
-
-class TestMasterpass(unittest.TestCase):
-    def test_create_from_nonce(self):
-        customer = Customer.create().customer
-        result = PaymentMethod.create({
-            "customer_id": customer.id,
-            "payment_method_nonce": Nonces.MasterpassVisa
-        })
-
-        self.assertTrue(result.is_success)
-
-        masterpass_card = result.payment_method
-        self.assertIsNotNone(masterpass_card.billing_address)
-        self.assertIsNotNone(masterpass_card.bin)
-        self.assertIsNotNone(masterpass_card.card_type)
-        self.assertIsNotNone(masterpass_card.cardholder_name)
-        self.assertIsNotNone(masterpass_card.commercial)
-        self.assertIsNotNone(masterpass_card.country_of_issuance)
-        self.assertIsNotNone(masterpass_card.created_at)
-        self.assertIsNotNone(masterpass_card.customer_id)
-        self.assertIsNotNone(masterpass_card.customer_location)
-        self.assertIsNotNone(masterpass_card.debit)
-        self.assertIsNotNone(masterpass_card.default)
-        self.assertIsNotNone(masterpass_card.durbin_regulated)
-        self.assertIsNotNone(masterpass_card.expiration_date)
-        self.assertIsNotNone(masterpass_card.expiration_month)
-        self.assertIsNotNone(masterpass_card.expiration_year)
-        self.assertIsNotNone(masterpass_card.expired)
-        self.assertIsNotNone(masterpass_card.healthcare)
-        self.assertIsNotNone(masterpass_card.image_url)
-        self.assertIsNotNone(masterpass_card.issuing_bank)
-        self.assertIsNotNone(masterpass_card.last_4)
-        self.assertIsNotNone(masterpass_card.masked_number)
-        self.assertIsNotNone(masterpass_card.payroll)
-        self.assertIsNotNone(masterpass_card.prepaid)
-        self.assertIsNotNone(masterpass_card.product_id)
-        self.assertIsNotNone(masterpass_card.subscriptions)
-        self.assertIsNotNone(masterpass_card.token)
-        self.assertIsNotNone(masterpass_card.unique_number_identifier)
-        self.assertIsNotNone(masterpass_card.updated_at)
-
-        customer = Customer.find(customer.id)
-        self.assertEqual(len(customer.masterpass_cards), 1)
-        self.assertEqual(result.payment_method.token, customer.masterpass_cards[0].token)
-
-    def test_search_for_transaction(self):
-        result = Transaction.sale({
-            "payment_method_nonce": Nonces.MasterpassVisa,
-            "amount": "1.23"
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.payment_instrument_type == PaymentInstrumentType.MasterpassCard
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(transaction.id, collection.first.id)
-
-    def test_create_transaction_from_nonce_and_vault(self):
-        customer = Customer.create().customer
-        result = Transaction.sale({
-            "payment_method_nonce": Nonces.MasterpassVisa,
-            "customer_id": customer.id,
-            "amount": "1.23",
-            "options": {
-                "store_in_vault": "true"
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        masterpass_card_details = result.transaction.masterpass_card_details
-
-        self.assertIsNotNone(masterpass_card_details.bin)
-        self.assertIsNotNone(masterpass_card_details.card_type)
-        self.assertIsNotNone(masterpass_card_details.cardholder_name)
-        self.assertIsNotNone(masterpass_card_details.commercial)
-        self.assertIsNotNone(masterpass_card_details.country_of_issuance)
-        self.assertIsNotNone(masterpass_card_details.debit)
-        self.assertIsNotNone(masterpass_card_details.durbin_regulated)
-        self.assertIsNotNone(masterpass_card_details.expiration_date)
-        self.assertIsNotNone(masterpass_card_details.expiration_year)
-        self.assertIsNotNone(masterpass_card_details.expiration_month)
-        self.assertIsNotNone(masterpass_card_details.healthcare)
-        self.assertIsNotNone(masterpass_card_details.image_url)
-        self.assertIsNotNone(masterpass_card_details.issuing_bank)
-        self.assertIsNotNone(masterpass_card_details.last_4)
-        self.assertIsNotNone(masterpass_card_details.payroll)
-        self.assertIsNotNone(masterpass_card_details.prepaid)
-        self.assertIsNotNone(masterpass_card_details.product_id)
-        self.assertIsNotNone(masterpass_card_details.token)
diff --git a/tests/integration/test_merchant.py b/tests/integration/test_merchant.py
deleted file mode 100644
index c1f41d2..0000000
--- a/tests/integration/test_merchant.py
+++ /dev/null
@@ -1,251 +0,0 @@
-from tests.test_helper import *
-from braintree.test.nonces import Nonces
-
-class TestMerchantGateway(unittest.TestCase):
-    def setUp(self):
-        self.gateway = BraintreeGateway(
-            client_id="client_id$development$signup_client_id",
-            client_secret="client_secret$development$signup_client_secret"
-        )
-
-    def test_create_merchant(self):
-        gateway = BraintreeGateway(
-            client_id="client_id$development$integration_client_id",
-            client_secret="client_secret$development$integration_client_secret"
-        )
-
-        result = gateway.merchant.create({
-            "email": "name@email.com",
-            "country_code_alpha3": "USA",
-            "payment_methods": ["credit_card", "paypal"]
-        })
-
-        merchant = result.merchant
-        self.assertIsNotNone(merchant.id)
-        self.assertEqual(merchant.email, "name@email.com")
-        self.assertEqual(merchant.country_code_alpha3, "USA")
-        self.assertEqual(merchant.country_code_alpha2, "US")
-        self.assertEqual(merchant.country_code_numeric, "840")
-        self.assertEqual(merchant.country_name, "United States of America")
-        self.assertEqual(merchant.company_name, "name@email.com")
-        self.assertTrue(result.is_success)
-
-        credentials = result.credentials
-        self.assertIsNotNone(credentials.access_token)
-        self.assertIsNotNone(credentials.expires_at)
-        self.assertEqual("bearer", credentials.token_type)
-
-    def test_returns_error_with_invalid_payment_methods(self):
-        gateway = BraintreeGateway(
-            client_id="client_id$development$integration_client_id",
-            client_secret="client_secret$development$integration_client_secret"
-        )
-
-        result = gateway.merchant.create({
-            "email": "name@email.com",
-            "country_code_alpha3": "USA",
-            "payment_methods": ["fake_money"]
-        })
-
-        self.assertFalse(result.is_success)
-        self.assertIn("One or more payment methods passed are not accepted.", result.message)
-
-        payment_method_errors = result.errors.for_object("merchant").on("payment_methods")
-        self.assertEqual(1, len(payment_method_errors))
-        self.assertEqual(payment_method_errors[0].code, ErrorCodes.Merchant.PaymentMethodsAreInvalid)
-
-    def test_create_paypal_only_merchant_that_accepts_multiple_currencies(self):
-        result = self.gateway.merchant.create({
-            "email": "name@email.com",
-            "country_code_alpha3": "USA",
-            "payment_methods": ["paypal"],
-            "currencies": ["GBP", "USD"],
-            "paypal_account": {
-                "client_id": "paypal_client_id",
-                "client_secret": "paypal_client_secret"
-            }
-        })
-
-        merchant = result.merchant
-        self.assertIsNotNone(merchant.id)
-        self.assertEqual(merchant.email, "name@email.com")
-        self.assertEqual(merchant.country_code_alpha3, "USA")
-        self.assertEqual(merchant.country_code_alpha2, "US")
-        self.assertEqual(merchant.country_code_numeric, "840")
-        self.assertEqual(merchant.country_name, "United States of America")
-        self.assertEqual(merchant.company_name, "name@email.com")
-        self.assertTrue(result.is_success)
-
-        credentials = result.credentials
-        self.assertIsNotNone(credentials.access_token)
-        self.assertIsNotNone(credentials.expires_at)
-        self.assertEqual("bearer", credentials.token_type)
-
-        merchant_accounts = merchant.merchant_accounts
-        self.assertEqual(2, len(merchant_accounts))
-
-        usd_merchant_account = [ma for ma in merchant_accounts if ma.id == "USD"][0]
-        self.assertTrue(usd_merchant_account.default)
-        self.assertEqual(usd_merchant_account.currency_iso_code, "USD")
-
-        gbp_merchant_account = [ma for ma in merchant_accounts if ma.id == "GBP"][0]
-        self.assertFalse(gbp_merchant_account.default)
-        self.assertEqual(gbp_merchant_account.currency_iso_code, "GBP")
-
-    def test_create_us_merchant_that_accepts_multiple_currencies(self):
-        result = self.gateway.merchant.create({
-            "email": "name@email.com",
-            "country_code_alpha3": "USA",
-            "payment_methods": ["credit_card", "paypal"],
-            "currencies": ["GBP", "USD"],
-            "paypal_account": {
-                "client_id": "paypal_client_id",
-                "client_secret": "paypal_client_secret"
-            }
-        })
-
-        merchant = result.merchant
-        self.assertIsNotNone(merchant.id)
-        self.assertEqual(merchant.email, "name@email.com")
-        self.assertEqual(merchant.country_code_alpha3, "USA")
-        self.assertEqual(merchant.country_code_alpha2, "US")
-        self.assertEqual(merchant.country_code_numeric, "840")
-        self.assertEqual(merchant.country_name, "United States of America")
-        self.assertEqual(merchant.company_name, "name@email.com")
-        self.assertTrue(result.is_success)
-
-        credentials = result.credentials
-        self.assertIsNotNone(credentials.access_token)
-        self.assertIsNotNone(credentials.expires_at)
-        self.assertEqual("bearer", credentials.token_type)
-
-        merchant_accounts = merchant.merchant_accounts
-        self.assertEqual(2, len(merchant_accounts))
-
-        usd_merchant_account = [ma for ma in merchant_accounts if ma.id == "USD"][0]
-        self.assertTrue(usd_merchant_account.default)
-        self.assertEqual(usd_merchant_account.currency_iso_code, "USD")
-
-        gbp_merchant_account = [ma for ma in merchant_accounts if ma.id == "GBP"][0]
-        self.assertFalse(gbp_merchant_account.default)
-        self.assertEqual(gbp_merchant_account.currency_iso_code, "GBP")
-
-    def test_create_eu_merchant_that_accepts_multiple_currencies(self):
-        result = self.gateway.merchant.create({
-            "email": "name@email.com",
-            "country_code_alpha3": "GBR",
-            "payment_methods": ["credit_card", "paypal"],
-            "currencies": ["GBP", "USD"],
-            "paypal_account": {
-                "client_id": "paypal_client_id",
-                "client_secret": "paypal_client_secret"
-            }
-        })
-
-        merchant = result.merchant
-        self.assertIsNotNone(merchant.id)
-        self.assertEqual(merchant.email, "name@email.com")
-        self.assertEqual(merchant.country_code_alpha3, "GBR")
-        self.assertEqual(merchant.country_code_alpha2, "GB")
-        self.assertEqual(merchant.country_code_numeric, "826")
-        self.assertEqual(merchant.country_name, "United Kingdom")
-        self.assertEqual(merchant.company_name, "name@email.com")
-        self.assertTrue(result.is_success)
-
-        credentials = result.credentials
-        self.assertIsNotNone(credentials.access_token)
-        self.assertIsNotNone(credentials.expires_at)
-        self.assertEqual("bearer", credentials.token_type)
-
-        merchant_accounts = merchant.merchant_accounts
-        self.assertEqual(2, len(merchant_accounts))
-
-        usd_merchant_account = [ma for ma in merchant_accounts if ma.id == "USD"][0]
-        self.assertFalse(usd_merchant_account.default)
-        self.assertEqual(usd_merchant_account.currency_iso_code, "USD")
-
-        gbp_merchant_account = [ma for ma in merchant_accounts if ma.id == "GBP"][0]
-        self.assertTrue(gbp_merchant_account.default)
-        self.assertEqual(gbp_merchant_account.currency_iso_code, "GBP")
-
-    def test_allows_creation_of_non_US_merchant_if_onboarding_application_is_internal(self):
-        result = self.gateway.merchant.create({
-            "email": "name@email.com",
-            "country_code_alpha3": "JPN",
-            "payment_methods": ["paypal"],
-            "paypal_account": {
-                "client_id": "paypal_client_id",
-                "client_secret": "paypal_client_secret"
-            }
-        })
-
-        merchant = result.merchant
-        self.assertIsNotNone(merchant.id)
-        self.assertEqual(merchant.email, "name@email.com")
-        self.assertEqual(merchant.country_code_alpha3, "JPN")
-        self.assertEqual(merchant.country_code_alpha2, "JP")
-        self.assertEqual(merchant.country_code_numeric, "392")
-        self.assertEqual(merchant.country_name, "Japan")
-        self.assertEqual(merchant.company_name, "name@email.com")
-        self.assertTrue(result.is_success)
-
-        credentials = result.credentials
-        self.assertIsNotNone(credentials.access_token)
-        self.assertIsNotNone(credentials.expires_at)
-        self.assertEqual("bearer", credentials.token_type)
-
-        merchant_accounts = merchant.merchant_accounts
-        self.assertEqual(1, len(merchant_accounts))
-
-        usd_merchant_account = merchant_accounts[0]
-        self.assertTrue(usd_merchant_account.default)
-        self.assertEqual(usd_merchant_account.currency_iso_code, "JPY")
-
-    def test_defaults_to_USD_for_non_US_merchant_if_onboarding_application_is_internal_and_country_currency_not_supported(self):
-        result = self.gateway.merchant.create({
-            "email": "name@email.com",
-            "country_code_alpha3": "YEM",
-            "payment_methods": ["paypal"],
-            "paypal_account": {
-                "client_id": "paypal_client_id",
-                "client_secret": "paypal_client_secret"
-            }
-        })
-
-        merchant = result.merchant
-        self.assertIsNotNone(merchant.id)
-        self.assertEqual(merchant.email, "name@email.com")
-        self.assertEqual(merchant.country_code_alpha3, "YEM")
-        self.assertEqual(merchant.country_code_alpha2, "YE")
-        self.assertEqual(merchant.country_code_numeric, "887")
-        self.assertEqual(merchant.country_name, "Yemen")
-        self.assertEqual(merchant.company_name, "name@email.com")
-        self.assertTrue(result.is_success)
-
-        credentials = result.credentials
-        self.assertIsNotNone(credentials.access_token)
-        self.assertIsNotNone(credentials.expires_at)
-        self.assertEqual("bearer", credentials.token_type)
-
-        merchant_accounts = merchant.merchant_accounts
-        self.assertEqual(1, len(merchant_accounts))
-
-        usd_merchant_account = merchant_accounts[0]
-        self.assertTrue(usd_merchant_account.default)
-        self.assertEqual(usd_merchant_account.currency_iso_code, "USD")
-
-    def test_returns_error_if_invalid_currency_is_passed(self):
-        result = self.gateway.merchant.create({
-            "email": "name@email.com",
-            "country_code_alpha3": "USA",
-            "payment_methods": ["credit_card"],
-            "currencies": ["GBP", "FAKE"],
-            "paypal_account": {
-                "client_id": "paypal_client_id",
-                "client_secret": "paypal_client_secret"
-            }
-        })
-        self.assertFalse(result.is_success)
-        currencies_errors = result.errors.for_object("merchant").on("currencies")
-        self.assertEqual(1, len(currencies_errors))
-        self.assertEqual(ErrorCodes.Merchant.CurrenciesAreInvalid, currencies_errors[0].code)
diff --git a/tests/integration/test_merchant_account.py b/tests/integration/test_merchant_account.py
deleted file mode 100644
index 9ad96ae..0000000
--- a/tests/integration/test_merchant_account.py
+++ /dev/null
@@ -1,549 +0,0 @@
-from tests.test_helper import *
-
-class TestMerchantAccount(unittest.TestCase):
-    DEPRECATED_APPLICATION_PARAMS = {
-        "applicant_details": {
-            "company_name": "Garbage Garage",
-            "first_name": "Joe",
-            "last_name": "Bloggs",
-            "email": "joe@bloggs.com",
-            "phone": "555-555-5555",
-            "address": {
-                "street_address": "123 Credibility St.",
-                "postal_code": "60606",
-                "locality": "Chicago",
-                "region": "IL",
-            },
-            "date_of_birth": "10/9/1980",
-            "ssn": "123-00-1234",
-            "tax_id": "123456789",
-            "routing_number": "122100024",
-            "account_number": "43759348798"
-        },
-        "tos_accepted": True,
-        "master_merchant_account_id": "sandbox_master_merchant_account"
-    }
-
-    VALID_APPLICATION_PARAMS = {
-        "individual": {
-            "first_name": "Joe",
-            "last_name": "Bloggs",
-            "email": "joe@bloggs.com",
-            "phone": "555-555-5555",
-            "address": {
-                "street_address": "123 Credibility St.",
-                "postal_code": "60606",
-                "locality": "Chicago",
-                "region": "IL",
-            },
-            "date_of_birth": "10/9/1980",
-            "ssn": "123-00-1234",
-        },
-        "business": {
-            "dba_name": "Garbage Garage",
-            "legal_name": "Junk Jymnasium",
-            "tax_id": "123456789",
-            "address": {
-                "street_address": "123 Reputation St.",
-                "postal_code": "40222",
-                "locality": "Louisville",
-                "region": "KY",
-            },
-        },
-        "funding": {
-            "routing_number": "122100024",
-            "account_number": "43759348798",
-            "destination": MerchantAccount.FundingDestination.Bank,
-            "descriptor": "Joes Bloggs KY",
-        },
-        "tos_accepted": True,
-        "master_merchant_account_id": "sandbox_master_merchant_account"
-    }
-
-    def test_create_accepts_deprecated_parameters(self):
-        result = MerchantAccount.create(self.DEPRECATED_APPLICATION_PARAMS)
-
-        self.assertTrue(result.is_success)
-        self.assertEqual(MerchantAccount.Status.Pending, result.merchant_account.status)
-        self.assertEqual("sandbox_master_merchant_account", result.merchant_account.master_merchant_account.id)
-
-    def test_create_application_with_valid_params_and_no_id(self):
-        result = MerchantAccount.create(self.VALID_APPLICATION_PARAMS)
-
-        self.assertTrue(result.is_success)
-        self.assertEqual(MerchantAccount.Status.Pending, result.merchant_account.status)
-        self.assertEqual("sandbox_master_merchant_account", result.merchant_account.master_merchant_account.id)
-
-    def test_create_allows_an_id_to_pass(self):
-        params_with_id = self.VALID_APPLICATION_PARAMS.copy()
-        rand = str(random.randint(1, 1000000))
-        params_with_id['id'] = 'sub_merchant_account_id' + rand
-        result = MerchantAccount.create(params_with_id)
-
-        self.assertTrue(result.is_success)
-        self.assertEqual(MerchantAccount.Status.Pending, result.merchant_account.status)
-        self.assertEqual(params_with_id['id'], result.merchant_account.id)
-        self.assertEqual("sandbox_master_merchant_account", result.merchant_account.master_merchant_account.id)
-
-    def test_create_handles_unsuccessful_results(self):
-        result = MerchantAccount.create({})
-        self.assertFalse(result.is_success)
-
-        merchant_account_id_errors = result.errors.for_object("merchant_account").on("master_merchant_account_id")
-        self.assertEqual(1, len(merchant_account_id_errors))
-        self.assertEqual(ErrorCodes.MerchantAccount.MasterMerchantAccountIdIsRequired, merchant_account_id_errors[0].code)
-
-    def test_create_requires_all_fields(self):
-        result = MerchantAccount.create(
-            {"master_merchant_account_id": "sandbox_master_merchant_account",
-             "applicant_details": {},
-            "tos_accepted": True}
-        )
-        self.assertFalse(result.is_success)
-
-        first_name_errors = result.errors.for_object("merchant_account").for_object("applicant_details").on("first_name")
-        self.assertEqual(1, len(first_name_errors))
-        self.assertEqual(ErrorCodes.MerchantAccount.ApplicantDetails.FirstNameIsRequired, first_name_errors[0].code)
-
-    def test_create_funding_destination_accepts_a_bank(self):
-        params = self.VALID_APPLICATION_PARAMS.copy()
-        params['funding']['destination'] = MerchantAccount.FundingDestination.Bank
-        result = MerchantAccount.create(params)
-        self.assertTrue(result.is_success)
-
-    def test_create_funding_destination_accepts_an_email(self):
-        params = self.VALID_APPLICATION_PARAMS.copy()
-        params['funding']['destination'] = MerchantAccount.FundingDestination.Email
-        params['funding']['email'] = "junkman@hotmail.com"
-        result = MerchantAccount.create(params)
-        self.assertTrue(result.is_success)
-
-    def test_create_funding_destination_accepts_a_mobile_phone(self):
-        params = self.VALID_APPLICATION_PARAMS.copy()
-        params['funding']['destination'] = MerchantAccount.FundingDestination.MobilePhone
-        params['funding']['mobile_phone'] = "1112223333"
-        result = MerchantAccount.create(params)
-        self.assertTrue(result.is_success)
-
-    def test_update_all_merchant_account_fields(self):
-        UPDATE_PARAMS = {
-            "individual": {
-                "first_name": "John",
-                "last_name": "Doe",
-                "email": "john.doe@example.com",
-                "phone": "312-555-1234",
-                "address": {
-                    "street_address": "123 Fake St",
-                    "postal_code": "60622",
-                    "locality": "Chicago",
-                    "region": "IL",
-                },
-                "date_of_birth": "1970-01-01",
-                "ssn": "987-65-4321",
-            },
-            "business": {
-                "dba_name": "James's Bloggs",
-                "legal_name": "James's Junkyard",
-                "tax_id": "987654321",
-                "address": {
-                    "street_address": "456 Fake St",
-                    "postal_code": "48104",
-                    "locality": "Ann Arbor",
-                    "region": "MI",
-                },
-            },
-            "funding": {
-                "routing_number": "071000013",
-                "account_number": "666666789",
-                "destination": MerchantAccount.FundingDestination.Email,
-                "email": "check@this.com",
-                "mobile_phone": "9998887777",
-                "descriptor": "Joes Bloggs MI",
-            }
-        }
-
-        result = MerchantAccount.update("sandbox_sub_merchant_account", UPDATE_PARAMS)
-        self.assertTrue(result.is_success)
-        self.assertEqual(result.merchant_account.status, "active")
-        self.assertEqual(result.merchant_account.id, "sandbox_sub_merchant_account")
-        self.assertEqual(result.merchant_account.master_merchant_account.id, "sandbox_master_merchant_account")
-        self.assertEqual(result.merchant_account.individual_details.first_name, "John")
-        self.assertEqual(result.merchant_account.individual_details.last_name, "Doe")
-        self.assertEqual(result.merchant_account.individual_details.email, "john.doe@example.com")
-        self.assertEqual(result.merchant_account.individual_details.date_of_birth, "1970-01-01")
-        self.assertEqual(result.merchant_account.individual_details.phone, "3125551234")
-        self.assertEqual(result.merchant_account.individual_details.address_details.street_address, "123 Fake St")
-        self.assertEqual(result.merchant_account.individual_details.address_details.locality, "Chicago")
-        self.assertEqual(result.merchant_account.individual_details.address_details.region, "IL")
-        self.assertEqual(result.merchant_account.individual_details.address_details.postal_code, "60622")
-        self.assertEqual(result.merchant_account.business_details.dba_name, "James's Bloggs")
-        self.assertEqual(result.merchant_account.business_details.legal_name, "James's Junkyard")
-        self.assertEqual(result.merchant_account.business_details.tax_id, "987654321")
-        self.assertEqual(result.merchant_account.business_details.address_details.street_address, "456 Fake St")
-        self.assertEqual(result.merchant_account.business_details.address_details.postal_code, "48104")
-        self.assertEqual(result.merchant_account.business_details.address_details.locality, "Ann Arbor")
-        self.assertEqual(result.merchant_account.business_details.address_details.region, "MI")
-        self.assertEqual(result.merchant_account.funding_details.routing_number, "071000013")
-        self.assertEqual(result.merchant_account.funding_details.account_number_last_4, "6789")
-        self.assertEqual(result.merchant_account.funding_details.destination, MerchantAccount.FundingDestination.Email)
-        self.assertEqual(result.merchant_account.funding_details.email, "check@this.com")
-        self.assertEqual(result.merchant_account.funding_details.mobile_phone, "9998887777")
-        self.assertEqual(result.merchant_account.funding_details.descriptor, "Joes Bloggs MI")
-
-    def test_update_does_not_require_all_fields(self):
-        result = MerchantAccount.update("sandbox_sub_merchant_account", {
-            "individual": {
-                "first_name": "Jose"
-            }
-        })
-        self.assertTrue(result.is_success)
-
-    def test_update_handles_validation_errors_for_blank_fields(self):
-        params = {
-            "individual": {
-                "first_name": "",
-                "last_name": "",
-                "email": "",
-                "phone": "",
-                "date_of_birth": "",
-                "ssn": "",
-                "address": {
-                    "street_address": "",
-                    "postal_code": "",
-                    "locality": "",
-                    "region": "",
-                },
-            },
-            "business": {
-                "legal_name": "",
-                "dba_name": "",
-                "tax_id": ""
-            },
-            "funding": {
-                "destination": "",
-                "routing_number": "",
-                "account_number": ""
-            }
-        }
-        result = MerchantAccount.update("sandbox_sub_merchant_account", params)
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(result.errors.for_object("merchant_account").for_object("individual").on("first_name")[0].code, ErrorCodes.MerchantAccount.Individual.FirstNameIsRequired)
-        self.assertEqual(result.errors.for_object("merchant_account").for_object("individual").on("last_name")[0].code, ErrorCodes.MerchantAccount.Individual.LastNameIsRequired)
-        self.assertEqual(result.errors.for_object("merchant_account").for_object("individual").on("date_of_birth")[0].code, ErrorCodes.MerchantAccount.Individual.DateOfBirthIsRequired)
-        self.assertEqual(result.errors.for_object("merchant_account").for_object("individual").on("email")[0].code, ErrorCodes.MerchantAccount.Individual.EmailAddressIsRequired)
-        self.assertEqual(result.errors.for_object("merchant_account").for_object("individual").for_object("address").on("street_address")[0].code, ErrorCodes.MerchantAccount.Individual.Address.StreetAddressIsRequired)
-        self.assertEqual(result.errors.for_object("merchant_account").for_object("individual").for_object("address").on("postal_code")[0].code, ErrorCodes.MerchantAccount.Individual.Address.PostalCodeIsRequired)
-        self.assertEqual(result.errors.for_object("merchant_account").for_object("individual").for_object("address").on("locality")[0].code, ErrorCodes.MerchantAccount.Individual.Address.LocalityIsRequired)
-        self.assertEqual(result.errors.for_object("merchant_account").for_object("individual").for_object("address").on("region")[0].code, ErrorCodes.MerchantAccount.Individual.Address.RegionIsRequired)
-        self.assertEqual(result.errors.for_object("merchant_account").for_object("funding").on("destination")[0].code, ErrorCodes.MerchantAccount.Funding.DestinationIsRequired)
-        self.assertEqual(result.errors.for_object("merchant_account").for_object("individual").on("first_name")[0].code, ErrorCodes.MerchantAccount.Individual.FirstNameIsRequired)
-        self.assertEqual(result.errors.for_object("merchant_account").for_object("individual").on("last_name")[0].code, ErrorCodes.MerchantAccount.Individual.LastNameIsRequired)
-        self.assertEqual(result.errors.for_object("merchant_account").for_object("individual").on("date_of_birth")[0].code, ErrorCodes.MerchantAccount.Individual.DateOfBirthIsRequired)
-        self.assertEqual(result.errors.for_object("merchant_account").for_object("individual").on("email")[0].code, ErrorCodes.MerchantAccount.Individual.EmailAddressIsRequired)
-        self.assertEqual(result.errors.for_object("merchant_account").for_object("individual").for_object("address").on("street_address")[0].code, ErrorCodes.MerchantAccount.Individual.Address.StreetAddressIsRequired)
-        self.assertEqual(result.errors.for_object("merchant_account").for_object("individual").for_object("address").on("postal_code")[0].code, ErrorCodes.MerchantAccount.Individual.Address.PostalCodeIsRequired)
-        self.assertEqual(result.errors.for_object("merchant_account").for_object("individual").for_object("address").on("locality")[0].code, ErrorCodes.MerchantAccount.Individual.Address.LocalityIsRequired)
-        self.assertEqual(result.errors.for_object("merchant_account").for_object("individual").for_object("address").on("region")[0].code, ErrorCodes.MerchantAccount.Individual.Address.RegionIsRequired)
-        self.assertEqual(result.errors.for_object("merchant_account").for_object("funding").on("destination")[0].code, ErrorCodes.MerchantAccount.Funding.DestinationIsRequired)
-        self.assertEqual(0, len(result.errors.for_object("merchant_account").on("base")))
-
-    def test_update_handles_validation_errors_for_invalid_fields(self):
-        params = {
-          "individual": {
-            "first_name": "<>",
-            "last_name": "<>",
-            "email": "bad",
-            "phone": "999",
-            "address": {
-              "street_address": "nope",
-              "postal_code": "1",
-              "region": "QQ",
-            },
-            "date_of_birth": "hah",
-            "ssn": "12345",
-          },
-          "business": {
-            "legal_name": "``{}",
-            "dba_name": "{}``",
-            "tax_id": "bad",
-            "address": {
-              "street_address": "nope",
-              "postal_code": "1",
-              "region": "QQ",
-            },
-          },
-          "funding": {
-            "destination": "MY WALLET",
-            "routing_number": "LEATHER",
-            "account_number": "BACK POCKET",
-            "email": "BILLFOLD",
-            "mobile_phone": "TRIFOLD"
-          },
-        }
-
-        result = MerchantAccount.update("sandbox_sub_merchant_account", params)
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(result.errors.for_object("merchant_account").for_object("individual").on("first_name")[0].code, ErrorCodes.MerchantAccount.Individual.FirstNameIsInvalid)
-        self.assertEqual(result.errors.for_object("merchant_account").for_object("individual").on("last_name")[0].code, ErrorCodes.MerchantAccount.Individual.LastNameIsInvalid)
-        self.assertEqual(result.errors.for_object("merchant_account").for_object("individual").on("email")[0].code, ErrorCodes.MerchantAccount.Individual.EmailAddressIsInvalid)
-        self.assertEqual(result.errors.for_object("merchant_account").for_object("individual").on("phone")[0].code, ErrorCodes.MerchantAccount.Individual.PhoneIsInvalid)
-        self.assertEqual(result.errors.for_object("merchant_account").for_object("individual").for_object("address").on("street_address")[0].code, ErrorCodes.MerchantAccount.Individual.Address.StreetAddressIsInvalid)
-        self.assertEqual(result.errors.for_object("merchant_account").for_object("individual").for_object("address").on("postal_code")[0].code, ErrorCodes.MerchantAccount.Individual.Address.PostalCodeIsInvalid)
-        self.assertEqual(result.errors.for_object("merchant_account").for_object("individual").for_object("address").on("region")[0].code, ErrorCodes.MerchantAccount.Individual.Address.RegionIsInvalid)
-        self.assertEqual(result.errors.for_object("merchant_account").for_object("individual").on("ssn")[0].code, ErrorCodes.MerchantAccount.Individual.SsnIsInvalid)
-
-        self.assertEqual(result.errors.for_object("merchant_account").for_object("business").on("legal_name")[0].code, ErrorCodes.MerchantAccount.Business.LegalNameIsInvalid)
-        self.assertEqual(result.errors.for_object("merchant_account").for_object("business").on("dba_name")[0].code, ErrorCodes.MerchantAccount.Business.DbaNameIsInvalid)
-        self.assertEqual(result.errors.for_object("merchant_account").for_object("business").on("tax_id")[0].code, ErrorCodes.MerchantAccount.Business.TaxIdIsInvalid)
-        self.assertEqual(result.errors.for_object("merchant_account").for_object("business").for_object("address").on("street_address")[0].code, ErrorCodes.MerchantAccount.Business.Address.StreetAddressIsInvalid)
-        self.assertEqual(result.errors.for_object("merchant_account").for_object("business").for_object("address").on("postal_code")[0].code, ErrorCodes.MerchantAccount.Business.Address.PostalCodeIsInvalid)
-        self.assertEqual(result.errors.for_object("merchant_account").for_object("business").for_object("address").on("region")[0].code, ErrorCodes.MerchantAccount.Business.Address.RegionIsInvalid)
-
-        self.assertEqual(result.errors.for_object("merchant_account").for_object("funding").on("destination")[0].code, ErrorCodes.MerchantAccount.Funding.DestinationIsInvalid)
-        self.assertEqual(result.errors.for_object("merchant_account").for_object("funding").on("routing_number")[0].code, ErrorCodes.MerchantAccount.Funding.RoutingNumberIsInvalid)
-        self.assertEqual(result.errors.for_object("merchant_account").for_object("funding").on("account_number")[0].code, ErrorCodes.MerchantAccount.Funding.AccountNumberIsInvalid)
-        self.assertEqual(result.errors.for_object("merchant_account").for_object("funding").on("email")[0].code, ErrorCodes.MerchantAccount.Funding.EmailAddressIsInvalid)
-        self.assertEqual(result.errors.for_object("merchant_account").for_object("funding").on("mobile_phone")[0].code, ErrorCodes.MerchantAccount.Funding.MobilePhoneIsInvalid)
-
-        self.assertEqual(0, len(result.errors.for_object("merchant_account").on("base")))
-
-    def test_update_handles_validation_errors_for_business_fields(self):
-        result = MerchantAccount.update("sandbox_sub_merchant_account", {
-            "business": {
-                "legal_name": "",
-                "tax_id": "111223333"
-                }
-            }
-        )
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(result.errors.for_object("merchant_account").for_object("business").on("legal_name")[0].code, ErrorCodes.MerchantAccount.Business.LegalNameIsRequiredWithTaxId)
-        self.assertEqual(result.errors.for_object("merchant_account").for_object("business").on("tax_id")[0].code, ErrorCodes.MerchantAccount.Business.TaxIdMustBeBlank)
-
-        result = MerchantAccount.update("sandbox_sub_merchant_account", {
-            "business": {
-                "legal_name": "legal name",
-                "tax_id": ""
-                }
-            }
-        )
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(result.errors.for_object("merchant_account").for_object("business").on("tax_id")[0].code, ErrorCodes.MerchantAccount.Business.TaxIdIsRequiredWithLegalName)
-
-    def test_update_handles_validation_errors_for_funding_fields(self):
-        result = MerchantAccount.update("sandbox_sub_merchant_account", {
-            "funding": {
-                "destination": MerchantAccount.FundingDestination.Bank,
-                "routing_number": "",
-                "account_number": ""
-                }
-            }
-        )
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(result.errors.for_object("merchant_account").for_object("funding").on("routing_number")[0].code, ErrorCodes.MerchantAccount.Funding.RoutingNumberIsRequired)
-        self.assertEqual(result.errors.for_object("merchant_account").for_object("funding").on("account_number")[0].code, ErrorCodes.MerchantAccount.Funding.AccountNumberIsRequired)
-
-        result = MerchantAccount.update("sandbox_sub_merchant_account", {
-            "funding": {
-                "destination": MerchantAccount.FundingDestination.Email,
-                "email": ""
-                }
-            }
-        )
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(result.errors.for_object("merchant_account").for_object("funding").on("email")[0].code, ErrorCodes.MerchantAccount.Funding.EmailAddressIsRequired)
-
-        result = MerchantAccount.update("sandbox_sub_merchant_account", {
-            "funding": {
-                "destination": MerchantAccount.FundingDestination.MobilePhone,
-                "mobile_phone": ""
-                }
-            }
-        )
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(result.errors.for_object("merchant_account").for_object("funding").on("mobile_phone")[0].code, ErrorCodes.MerchantAccount.Funding.MobilePhoneIsRequired)
-
-    def test_find(self):
-        result = MerchantAccount.create(self.VALID_APPLICATION_PARAMS)
-        self.assertTrue(result.is_success)
-        merchant_account_id = result.merchant_account.id
-        MerchantAccount.find(merchant_account_id)
-
-    def test_retrieves_master_merchant_account_currency_iso_code(self):
-        merchant_account = MerchantAccount.find("sandbox_master_merchant_account")
-        self.assertEqual(merchant_account.currency_iso_code, "USD")
-
-    def test_return_all_merchant_accounts(self):
-        gateway = BraintreeGateway(
-            client_id="client_id$development$integration_client_id",
-            client_secret="client_secret$development$integration_client_secret"
-        )
-
-        code = TestHelper.create_grant(gateway, {
-            "merchant_public_id": "integration_merchant_id",
-            "scope": "read_write"
-        })
-
-        result = gateway.oauth.create_token_from_code({
-            "code": code
-        })
-
-        gateway = BraintreeGateway(
-            access_token=result.credentials.access_token,
-            environment=Environment.Development
-        )
-
-        result = gateway.merchant_account.all()
-        merchant_accounts = [ma for ma in result.merchant_accounts.items]
-        self.assertTrue(len(merchant_accounts) > 20)
-
-    def test_returns_merchant_account_with_correct_attributes(self):
-        gateway = BraintreeGateway(
-            client_id="client_id$development$integration_client_id",
-            client_secret="client_secret$development$integration_client_secret"
-        )
-
-        result = gateway.merchant.create({
-            "email": "name@email.com",
-            "country_code_alpha3": "USA",
-            "payment_methods": ["credit_card", "paypal"]
-        })
-
-        gateway = BraintreeGateway(
-            access_token=result.credentials.access_token
-        )
-
-        result = gateway.merchant_account.all()
-        merchant_accounts = [ma for ma in result.merchant_accounts.items]
-        self.assertEqual(len(merchant_accounts), 1)
-
-        merchant_account = merchant_accounts[0]
-        self.assertEqual(merchant_account.currency_iso_code, "USD")
-        self.assertEqual(merchant_account.status, MerchantAccount.Status.Active)
-        self.assertTrue(merchant_account.default)
-
-    @raises(NotFoundError)
-    def test_find_404(self):
-        MerchantAccount.find("not_a_real_id")
-
-    def test_merchant_account_create_for_currency(self):
-        self.gateway = BraintreeGateway(
-            client_id="client_id$development$integration_client_id",
-            client_secret="client_secret$development$integration_client_secret"
-        )
-
-        result = self.gateway.merchant.create({
-            "email": "name@email.com",
-            "country_code_alpha3": "USA",
-            "payment_methods": ["credit_card", "paypal"]
-        })
-
-        gateway = BraintreeGateway(
-            access_token=result.credentials.access_token,
-        )
-
-        result = gateway.merchant_account.create_for_currency({
-            "currency": "GBP",
-            "id": "custom_id"
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertEqual(result.merchant_account.currency_iso_code, "GBP")
-        self.assertEqual(result.merchant_account.id, "custom_id")
-
-    def test_merchant_account_create_for_currency_handles_invalid_currency(self):
-        self.gateway = BraintreeGateway(
-            client_id="client_id$development$integration_client_id",
-            client_secret="client_secret$development$integration_client_secret"
-        )
-
-        result = self.gateway.merchant.create({
-            "email": "name@email.com",
-            "country_code_alpha3": "USA",
-            "payment_methods": ["credit_card", "paypal"]
-        })
-
-        gateway = BraintreeGateway(
-            access_token=result.credentials.access_token,
-        )
-
-        result = gateway.merchant_account.create_for_currency({
-            "currency": "DOES_NOT_COMPUTE"
-        })
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(result.errors.for_object("merchant").on("currency")[0].code, ErrorCodes.Merchant.CurrencyIsInvalid)
-
-    def test_merchant_account_create_for_currency_handles_currency_requirement(self):
-        self.gateway = BraintreeGateway(
-            client_id="client_id$development$integration_client_id",
-            client_secret="client_secret$development$integration_client_secret"
-        )
-
-        result = self.gateway.merchant.create({
-            "email": "name@email.com",
-            "country_code_alpha3": "USA",
-            "payment_methods": ["credit_card", "paypal"]
-        })
-
-        gateway = BraintreeGateway(
-            access_token=result.credentials.access_token,
-        )
-
-        result = gateway.merchant_account.create_for_currency({})
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(result.errors.for_object("merchant").on("currency")[0].code, ErrorCodes.Merchant.CurrencyIsRequired)
-
-    def test_merchant_account_create_for_currency_merchant_account_already_existing_for_currency(self):
-        self.gateway = BraintreeGateway(
-            client_id="client_id$development$integration_client_id",
-            client_secret="client_secret$development$integration_client_secret"
-        )
-
-        result = self.gateway.merchant.create({
-            "email": "name@email.com",
-            "country_code_alpha3": "USA",
-            "payment_methods": ["credit_card", "paypal"]
-        })
-
-        gateway = BraintreeGateway(
-            access_token=result.credentials.access_token,
-        )
-
-        result = gateway.merchant_account.create_for_currency({
-            "currency": "USD",
-        })
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(result.errors.for_object("merchant").on("currency")[0].code, ErrorCodes.Merchant.MerchantAccountExistsForCurrency)
-
-    def test_merchant_account_create_for_currency_merchant_account_already_existing_for_id(self):
-        self.gateway = BraintreeGateway(
-            client_id="client_id$development$integration_client_id",
-            client_secret="client_secret$development$integration_client_secret"
-        )
-
-        result = self.gateway.merchant.create({
-            "email": "name@email.com",
-            "country_code_alpha3": "USA",
-            "payment_methods": ["credit_card", "paypal"]
-        })
-
-        gateway = BraintreeGateway(
-            access_token=result.credentials.access_token,
-        )
-
-        result = gateway.merchant_account.create_for_currency({
-            "currency": "GBP",
-            "id": result.merchant.merchant_accounts[0].id
-        })
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(result.errors.for_object("merchant").on("id")[0].code, ErrorCodes.Merchant.MerchantAccountExistsForId)
diff --git a/tests/integration/test_oauth.py b/tests/integration/test_oauth.py
deleted file mode 100644
index c3e6939..0000000
--- a/tests/integration/test_oauth.py
+++ /dev/null
@@ -1,183 +0,0 @@
-from tests.test_helper import *
-from braintree.test.nonces import Nonces
-import sys
-if sys.version_info[0] == 2:
-    import urlparse
-else:
-    import urllib.parse as urlparse
-
-class TestOAuthGateway(unittest.TestCase):
-    def setUp(self):
-        self.gateway = BraintreeGateway(
-            client_id="client_id$development$integration_client_id",
-            client_secret="client_secret$development$integration_client_secret"
-        )
-
-    def test_create_token_from_code(self):
-        code = TestHelper.create_grant(self.gateway, {
-            "merchant_public_id": "integration_merchant_id",
-            "scope": "read_write"
-        })
-
-        result = self.gateway.oauth.create_token_from_code({
-            "code": code
-        })
-
-        self.assertTrue(result.is_success)
-
-        credentials = result.credentials
-        self.assertIsNotNone(credentials.access_token)
-        self.assertIsNotNone(credentials.refresh_token)
-        self.assertIsNotNone(credentials.expires_at)
-        self.assertEqual("bearer", credentials.token_type)
-
-
-    def test_create_token_from_code_with_bad_parameters(self):
-        result = self.gateway.oauth.create_token_from_code({
-            "code": "bad_code",
-            "scope": "read_write"
-        })
-
-        self.assertFalse(result.is_success)
-        self.assertIn(result.message, "Invalid grant: code not found")
-
-        credentials_code_errors = result.errors.for_object("credentials").on("code")
-        self.assertEqual(1, len(credentials_code_errors))
-        self.assertEqual(ErrorCodes.OAuth.InvalidGrant, credentials_code_errors[0].code)
-
-    def test_create_token_from_code_returns_helpful_error_with_bad_credentials(self):
-        gateway = BraintreeGateway(
-            access_token="access_token$development$integration_merchant_id$fb27c79dd",
-        )
-
-        with self.assertRaises(ConfigurationError) as error:
-            gateway.oauth.create_token_from_code({
-                "code": "some_code",
-                "scope": "read_write"
-            })
-
-        config_error = error.exception
-        self.assertIn("client_id and client_secret are required", str(config_error))
-
-    def test_create_token_from_refresh_token(self):
-        code = TestHelper.create_grant(self.gateway, {
-            "merchant_public_id": "integration_merchant_id",
-            "scope": "read_write"
-        })
-
-        refresh_token = self.gateway.oauth.create_token_from_code({
-            "code": code,
-            "scope": "read_write"
-        }).credentials.refresh_token
-
-        result = self.gateway.oauth.create_token_from_refresh_token({
-            "refresh_token": refresh_token
-        })
-
-        self.assertTrue(result.is_success)
-
-        credentials = result.credentials
-        self.assertIsNotNone(credentials.access_token)
-        self.assertIsNotNone(credentials.refresh_token)
-        self.assertIsNotNone(credentials.expires_at)
-        self.assertEqual("bearer", credentials.token_type)
-
-    def test_revoke_access_token(self):
-        code = TestHelper.create_grant(self.gateway, {
-            "merchant_public_id": "integration_merchant_id",
-            "scope": "read_write"
-        })
-
-        access_token = self.gateway.oauth.create_token_from_code({
-            "code": code,
-            "scope": "read_write"
-        }).credentials.access_token
-
-        result = self.gateway.oauth.revoke_access_token(access_token)
-
-        self.assertTrue(result.is_success)
-
-        with self.assertRaises(AuthenticationError):
-            gateway = BraintreeGateway(access_token=access_token)
-
-            gateway.customer.create()
-
-    def test_connect_url(self):
-        connect_url = self.gateway.oauth.connect_url({
-             "merchant_id": "integration_merchant_id",
-             "redirect_uri": "http://bar.example.com",
-             "scope": "read_write",
-             "state": "baz_state",
-             "landing_page": "login",
-             "login_only": "true",
-             "user": {
-               "country": "USA",
-               "email": "foo@example.com",
-               "first_name": "Bob",
-               "last_name": "Jones",
-               "phone": "555-555-5555",
-               "dob_year": "1970",
-               "dob_month": "01",
-               "dob_day": "01",
-               "street_address": "222 W Merchandise Mart",
-               "locality": "Chicago",
-               "region": "IL",
-               "postal_code": "60606"
-             },
-             "business": {
-               "name": "14 Ladders",
-               "registered_as": "14.0 Ladders",
-               "industry": "Ladders",
-               "description": "We sell the best ladders",
-               "street_address": "111 N Canal",
-               "locality": "Chicago",
-               "region": "IL",
-               "postal_code": "60606",
-               "country": "USA",
-               "annual_volume_amount": "1000000",
-               "average_transaction_amount": "100",
-               "maximum_transaction_amount": "10000",
-               "ship_physical_goods": "true",
-               "fulfillment_completed_in": 7,
-               "currency": "USD",
-               "website": "http://example.com"
-            },
-            "payment_methods": ["credit_card", "paypal"]
-        })
-        query_string = urlparse.urlparse(connect_url)[4]
-        params = urlparse.parse_qs(query_string)
-
-        self.assertEqual(params["merchant_id"], ["integration_merchant_id"])
-        self.assertEqual(params["client_id"], ["client_id$development$integration_client_id"])
-        self.assertEqual(params["redirect_uri"], ["http://bar.example.com"])
-        self.assertEqual(params["scope"], ["read_write"])
-        self.assertEqual(params["state"], ["baz_state"])
-        self.assertEqual(params["landing_page"], ["login"])
-        self.assertEqual(params["login_only"], ["true"])
-
-        self.assertEqual(params["user[country]"], ["USA"])
-        self.assertEqual(params["business[name]"], ["14 Ladders"])
-        self.assertEqual(params["payment_methods[]"], ["credit_card", "paypal"])
-
-    def test_connect_url_limits_payment_methods(self):
-        connect_url = self.gateway.oauth.connect_url({
-             "merchant_id": "integration_merchant_id",
-             "redirect_uri": "http://bar.example.com",
-             "scope": "read_write",
-             "state": "baz_state",
-            "payment_methods": ["credit_card"]
-        })
-        query_string = urlparse.urlparse(connect_url)[4]
-        params = urlparse.parse_qs(query_string)
-
-        self.assertEqual(params["merchant_id"], ["integration_merchant_id"])
-        self.assertEqual(params["client_id"], ["client_id$development$integration_client_id"])
-        self.assertEqual(params["redirect_uri"], ["http://bar.example.com"])
-        self.assertEqual(params["payment_methods[]"], ["credit_card"])
-
-    def test_connect_url_doesnt_modify_options(self):
-        options = {"payment_methods": ["credit_card"]}
-
-        connect_url = self.gateway.oauth.connect_url(options)
-
-        self.assertEqual(options, {"payment_methods": ["credit_card"]})
diff --git a/tests/integration/test_payment_method.py b/tests/integration/test_payment_method.py
deleted file mode 100644
index d23a8eb..0000000
--- a/tests/integration/test_payment_method.py
+++ /dev/null
@@ -1,1386 +0,0 @@
-import time
-from datetime import datetime
-from tests.test_helper import *
-from braintree.test.credit_card_numbers import CreditCardNumbers
-from braintree.test.nonces import Nonces
-
-class TestPaymentMethod(unittest.TestCase):
-    def test_create_with_three_d_secure_nonce(self):
-        customer_id = Customer.create().customer.id
-        result = PaymentMethod.create({
-            "customer_id": customer_id,
-            "payment_method_nonce": Nonces.ThreeDSecureVisaFullAuthentication,
-            "options": {
-                "verify_card": "true",
-            }
-        })
-
-        self.assertTrue(result.is_success)
-
-        three_d_secure_info = result.payment_method.verification.three_d_secure_info
-
-        self.assertEqual("Y", three_d_secure_info.enrolled)
-        self.assertEqual("authenticate_successful", three_d_secure_info.status)
-        self.assertEqual(True, three_d_secure_info.liability_shifted)
-        self.assertEqual(True, three_d_secure_info.liability_shift_possible)
-        self.assertEqual("cavv_value", three_d_secure_info.cavv)
-        self.assertEqual("xid_value", three_d_secure_info.xid)
-        self.assertEqual(None, three_d_secure_info.ds_transaction_id)
-        self.assertEqual("05", three_d_secure_info.eci_flag)
-        self.assertEqual("1.0.2", three_d_secure_info.three_d_secure_version)
-
-    def test_create_with_paypal_future_payments_nonce(self):
-        customer_id = Customer.create().customer.id
-        result = PaymentMethod.create({
-            "customer_id": customer_id,
-            "payment_method_nonce": Nonces.PayPalFuturePayment
-        })
-
-        self.assertTrue(result.is_success)
-        created_account = result.payment_method
-        self.assertEqual(PayPalAccount, created_account.__class__)
-        self.assertEqual("jane.doe@example.com", created_account.email)
-        self.assertNotEqual(created_account.image_url, None)
-
-        found_account = PaymentMethod.find(result.payment_method.token)
-        self.assertNotEqual(None, found_account)
-        self.assertEqual(created_account.token, found_account.token)
-        self.assertEqual(created_account.customer_id, found_account.customer_id)
-
-    def test_create_with_paypal_order_payment_nonce_and_paypal_options(self):
-        customer_id = Customer.create().customer.id
-
-        http = ClientApiHttp.create()
-        status_code, payment_method_nonce = http.get_paypal_nonce({
-            "intent": "order",
-            "payment-token": "fake-paypal-payment-token",
-            "payer-id": "fake-paypal-payer-id"
-        })
-
-        result = PaymentMethod.create({
-            "customer_id": customer_id,
-            "payment_method_nonce": payment_method_nonce,
-            "options": {
-                "paypal": {
-                    "payee_email": "payee@example.com",
-                    "order_id": "merchant-order-id",
-                    "custom_field": "custom merchant field",
-                    "description": "merchant description",
-                    "amount": "1.23",
-                    "shipping": {
-                        "first_name": "Andrew",
-                        "last_name": "Mason",
-                        "company": "Braintree",
-                        "street_address": "456 W Main St",
-                        "extended_address": "Apt 2F",
-                        "locality": "Bartlett",
-                        "region": "IL",
-                        "postal_code": "60103",
-                        "country_name": "Mexico",
-                        "country_code_alpha2": "MX",
-                        "country_code_alpha3": "MEX",
-                        "country_code_numeric": "484"
-                    },
-                },
-            },
-        })
-
-        self.assertTrue(result.is_success)
-        created_account = result.payment_method
-        self.assertEqual(PayPalAccount, created_account.__class__)
-        self.assertEqual("bt_buyer_us@paypal.com", created_account.email)
-        self.assertNotEqual(created_account.image_url, None)
-        self.assertNotEqual(created_account.payer_id, None)
-
-        found_account = PaymentMethod.find(result.payment_method.token)
-        self.assertNotEqual(None, found_account)
-        self.assertEqual(created_account.token, found_account.token)
-        self.assertEqual(created_account.customer_id, found_account.customer_id)
-        self.assertEqual(created_account.payer_id, found_account.payer_id)
-
-    def test_create_with_paypal_refresh_token(self):
-        customer_id = Customer.create().customer.id
-        result = PaymentMethod.create({
-            "customer_id": customer_id,
-            "paypal_refresh_token": "PAYPAL_REFRESH_TOKEN",
-        })
-
-        self.assertTrue(result.is_success)
-        created_account = result.payment_method
-        self.assertEqual(PayPalAccount, created_account.__class__)
-        self.assertEqual("B_FAKE_ID", created_account.billing_agreement_id)
-        self.assertNotEqual(created_account.payer_id, None)
-
-        found_account = PaymentMethod.find(result.payment_method.token)
-        self.assertNotEqual(None, found_account)
-        self.assertEqual(created_account.token, found_account.token)
-        self.assertEqual(created_account.customer_id, found_account.customer_id)
-        self.assertEqual(created_account.billing_agreement_id, found_account.billing_agreement_id)
-        self.assertEqual(created_account.payer_id, found_account.payer_id)
-
-    def test_create_with_paypal_refresh_token_without_upgrade(self):
-        customer_id = Customer.create().customer.id
-        result = PaymentMethod.create({
-            "customer_id": customer_id,
-            "paypal_refresh_token": "PAYPAL_REFRESH_TOKEN",
-            "paypal_vault_without_upgrade": True,
-        })
-
-        self.assertTrue(result.is_success)
-        created_account = result.payment_method
-        self.assertEqual(PayPalAccount, created_account.__class__)
-        self.assertEqual(created_account.billing_agreement_id, None)
-
-        found_account = PaymentMethod.find(result.payment_method.token)
-        self.assertNotEqual(None, found_account)
-        self.assertEqual(created_account.token, found_account.token)
-        self.assertEqual(created_account.customer_id, found_account.customer_id)
-        self.assertEqual(created_account.billing_agreement_id, found_account.billing_agreement_id)
-
-    def test_create_returns_validation_failures(self):
-        http = ClientApiHttp.create()
-        status_code, nonce = http.get_paypal_nonce({
-            "options": {"validate": False}
-        })
-        self.assertEqual(202, status_code)
-        result = PaymentMethod.create({
-            "payment_method_nonce": nonce
-        })
-
-        self.assertFalse(result.is_success)
-        paypal_error_codes = [
-            error.code for error in result.errors.for_object("paypal_account").on("base")
-        ]
-        self.assertTrue(ErrorCodes.PayPalAccount.ConsentCodeOrAccessTokenIsRequired in paypal_error_codes)
-        customer_error_codes = [
-            error.code for error in result.errors.for_object("paypal_account").on("customer_id")
-        ]
-        self.assertTrue(ErrorCodes.PayPalAccount.CustomerIdIsRequiredForVaulting in customer_error_codes)
-
-    def test_create_and_make_default(self):
-        customer_id = Customer.create().customer.id
-        credit_card_result = CreditCard.create({
-            "customer_id": customer_id,
-            "number": "4111111111111111",
-            "expiration_date": "05/2014",
-        })
-        self.assertTrue(credit_card_result.is_success)
-
-        result = PaymentMethod.create({
-            "customer_id": customer_id,
-            "payment_method_nonce": Nonces.PayPalFuturePayment,
-            "options": {"make_default": True},
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertTrue(result.payment_method.default)
-
-    def test_create_and_set_token(self):
-        customer_id = Customer.create().customer.id
-        token = str(random.randint(1, 1000000))
-
-        result = PaymentMethod.create({
-            "customer_id": customer_id,
-            "payment_method_nonce": Nonces.PayPalFuturePayment,
-            "token": token
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertEqual(token, result.payment_method.token)
-
-    def test_create_with_paypal_one_time_nonce_fails(self):
-        customer_id = Customer.create().customer.id
-        result = PaymentMethod.create({
-            "customer_id": customer_id,
-            "payment_method_nonce": Nonces.PayPalOneTimePayment
-        })
-
-        self.assertFalse(result.is_success)
-        base_errors = result.errors.for_object("paypal_account").on("base")
-        self.assertEqual(1, len(base_errors))
-        self.assertEqual(ErrorCodes.PayPalAccount.CannotVaultOneTimeUsePayPalAccount, base_errors[0].code)
-
-    def test_create_with_credit_card_nonce(self):
-        http = ClientApiHttp.create()
-        status_code, nonce = http.get_credit_card_nonce({
-            "number": "4111111111111111",
-            "expirationMonth": "12",
-            "expirationYear": "2020",
-            "options": {"validate": False}
-        })
-        self.assertEqual(202, status_code)
-
-        customer_id = Customer.create().customer.id
-
-        result = PaymentMethod.create({
-            "customer_id": customer_id,
-            "payment_method_nonce": nonce
-        })
-
-        self.assertTrue(result.is_success)
-        created_credit_card = result.payment_method
-        self.assertEqual(CreditCard, created_credit_card.__class__)
-        self.assertEqual("411111", created_credit_card.bin)
-
-        found_credit_card = PaymentMethod.find(result.payment_method.token)
-        self.assertNotEqual(None, found_credit_card)
-        self.assertEqual(found_credit_card.token, created_credit_card.token)
-        self.assertEqual(found_credit_card.customer_id, created_credit_card.customer_id)
-
-    def test_create_with_fake_apple_pay_nonce(self):
-        customer_id = Customer.create().customer.id
-        result = PaymentMethod.create({
-            "customer_id": customer_id,
-            "payment_method_nonce": Nonces.ApplePayMasterCard
-        })
-
-        self.assertTrue(result.is_success)
-        apple_pay_card = result.payment_method
-        self.assertIsInstance(apple_pay_card, ApplePayCard)
-        self.assertNotEqual(apple_pay_card.bin, None)
-        self.assertNotEqual(apple_pay_card.token, None)
-        self.assertEqual(apple_pay_card.customer_id, customer_id)
-        self.assertEqual(ApplePayCard.CardType.MasterCard, apple_pay_card.card_type)
-        self.assertEqual("MasterCard 0017", apple_pay_card.payment_instrument_name)
-        self.assertEqual("MasterCard 0017", apple_pay_card.source_description)
-        self.assertTrue(apple_pay_card.default)
-        self.assertIn("apple_pay", apple_pay_card.image_url)
-        self.assertTrue(int(apple_pay_card.expiration_month) > 0)
-        self.assertTrue(int(apple_pay_card.expiration_year) > 0)
-
-    def test_create_with_fake_android_pay_proxy_card_nonce(self):
-        customer_id = Customer.create().customer.id
-        result = PaymentMethod.create({
-            "customer_id": customer_id,
-            "payment_method_nonce": Nonces.AndroidPayCardDiscover
-        })
-
-        self.assertTrue(result.is_success)
-        android_pay_card = result.payment_method
-        self.assertIsInstance(android_pay_card, AndroidPayCard)
-        self.assertNotEqual(android_pay_card.token, None)
-        self.assertEqual(customer_id, android_pay_card.customer_id)
-        self.assertEqual(CreditCard.CardType.Discover, android_pay_card.virtual_card_type)
-        self.assertEqual("1117", android_pay_card.virtual_card_last_4)
-        self.assertEqual("Discover 1111", android_pay_card.source_description)
-        self.assertEqual(CreditCard.CardType.Discover, android_pay_card.source_card_type)
-        self.assertEqual("1111", android_pay_card.source_card_last_4)
-        self.assertEqual("1117", android_pay_card.last_4)
-        self.assertEqual(CreditCard.CardType.Discover, android_pay_card.card_type)
-        self.assertTrue(android_pay_card.default)
-        self.assertIn("android_pay", android_pay_card.image_url)
-        self.assertTrue(int(android_pay_card.expiration_month) > 0)
-        self.assertTrue(int(android_pay_card.expiration_year) > 0)
-        self.assertIsInstance(android_pay_card.created_at, datetime)
-        self.assertIsInstance(android_pay_card.updated_at, datetime)
-        self.assertEqual("601111", android_pay_card.bin)
-        self.assertEqual("google_transaction_id", android_pay_card.google_transaction_id)
-
-    def test_create_with_fake_android_pay_network_token_nonce(self):
-        customer_id = Customer.create().customer.id
-        result = PaymentMethod.create({
-            "customer_id": customer_id,
-            "payment_method_nonce": Nonces.AndroidPayCardMasterCard
-        })
-
-        self.assertTrue(result.is_success)
-        android_pay_card = result.payment_method
-        self.assertIsInstance(android_pay_card, AndroidPayCard)
-        self.assertNotEqual(android_pay_card.token, None)
-        self.assertEqual(customer_id, android_pay_card.customer_id)
-        self.assertEqual(CreditCard.CardType.MasterCard, android_pay_card.virtual_card_type)
-        self.assertEqual("4444", android_pay_card.virtual_card_last_4)
-        self.assertEqual("MasterCard 4444", android_pay_card.source_description)
-        self.assertEqual(CreditCard.CardType.MasterCard, android_pay_card.source_card_type)
-        self.assertEqual("4444", android_pay_card.source_card_last_4)
-        self.assertEqual("4444", android_pay_card.last_4)
-        self.assertEqual(CreditCard.CardType.MasterCard, android_pay_card.card_type)
-        self.assertTrue(android_pay_card.default)
-        self.assertIn("android_pay", android_pay_card.image_url)
-        self.assertTrue(int(android_pay_card.expiration_month) > 0)
-        self.assertTrue(int(android_pay_card.expiration_year) > 0)
-        self.assertIsInstance(android_pay_card.created_at, datetime)
-        self.assertIsInstance(android_pay_card.updated_at, datetime)
-        self.assertEqual("555555", android_pay_card.bin)
-        self.assertEqual("google_transaction_id", android_pay_card.google_transaction_id)
-
-    def test_create_with_fake_amex_express_checkout_card_nonce(self):
-        customer_id = Customer.create().customer.id
-        result = PaymentMethod.create({
-            "customer_id": customer_id,
-            "payment_method_nonce": Nonces.AmexExpressCheckoutCard
-        })
-
-        self.assertTrue(result.is_success)
-        amex_express_checkout_card = result.payment_method
-        self.assertIsInstance(amex_express_checkout_card, AmexExpressCheckoutCard)
-        self.assertNotEqual(amex_express_checkout_card.token, None)
-        self.assertTrue(amex_express_checkout_card.default)
-        self.assertEqual("American Express", amex_express_checkout_card.card_type)
-        self.assertRegexpMatches(amex_express_checkout_card.bin, r"\A\d{6}\Z")
-        self.assertRegexpMatches(amex_express_checkout_card.expiration_month, r"\A\d{2}\Z")
-        self.assertRegexpMatches(amex_express_checkout_card.expiration_year, r"\A\d{4}\Z")
-        self.assertRegexpMatches(amex_express_checkout_card.card_member_number, r"\A\d{4}\Z")
-        self.assertRegexpMatches(amex_express_checkout_card.card_member_expiry_date, r"\A\d{2}/\d{2}\Z")
-        self.assertRegexpMatches(amex_express_checkout_card.source_description, r"\AAmEx \d{4}\Z")
-        self.assertRegexpMatches(amex_express_checkout_card.image_url, r"\.png")
-        self.assertEqual(customer_id, amex_express_checkout_card.customer_id)
-
-    def test_create_with_venmo_account_nonce(self):
-        customer_id = Customer.create().customer.id
-        result = PaymentMethod.create({
-            "customer_id": customer_id,
-            "payment_method_nonce": Nonces.VenmoAccount,
-        })
-
-        self.assertTrue(result.is_success)
-        venmo_account = result.payment_method
-        self.assertIsInstance(venmo_account, VenmoAccount)
-
-        self.assertTrue(venmo_account.default)
-        self.assertIsNotNone(venmo_account.token)
-        self.assertEqual("venmojoe", venmo_account.username)
-        self.assertEqual("Venmo-Joe-1", venmo_account.venmo_user_id)
-        self.assertEqual("Venmo Account: venmojoe", venmo_account.source_description)
-        self.assertRegexpMatches(venmo_account.image_url, r"\.png")
-        self.assertEqual(customer_id, venmo_account.customer_id)
-
-    def test_create_with_abstract_payment_method_nonce(self):
-        customer_id = Customer.create().customer.id
-        result = PaymentMethod.create({
-            "customer_id": customer_id,
-            "payment_method_nonce": Nonces.AbstractTransactable
-        })
-
-        self.assertTrue(result.is_success)
-        payment_method = result.payment_method
-        self.assertNotEqual(None, payment_method)
-        self.assertNotEqual(None, payment_method.token)
-        self.assertEqual(customer_id, payment_method.customer_id)
-
-    def test_create_with_custom_card_verification_amount(self):
-        config = Configuration.instantiate()
-        customer_id = Customer.create().customer.id
-        client_token = json.loads(TestHelper.generate_decoded_client_token())
-        authorization_fingerprint = client_token["authorizationFingerprint"]
-        http = ClientApiHttp(config, {
-                    "authorization_fingerprint": authorization_fingerprint,
-                    "shared_customer_identifier": "fake_identifier",
-                    "shared_customer_identifier_type": "testing"
-                })
-        status_code, nonce = http.get_credit_card_nonce({
-            "number": "4000111111111115",
-            "expirationMonth": "11",
-            "expirationYear": "2099"
-        })
-        self.assertTrue(status_code == 201)
-
-        result = PaymentMethod.create({
-            "customer_id": customer_id,
-            "payment_method_nonce": nonce,
-            "options": {
-                "verify_card": "true",
-                "verification_amount": "1.02"
-            }
-        })
-
-        self.assertFalse(result.is_success)
-        verification = result.credit_card_verification
-        self.assertEqual(CreditCardVerification.Status.ProcessorDeclined, verification.status)
-
-    def test_create_respects_verify_card_and_verification_merchant_account_id_when_outside_nonce(self):
-        config = Configuration.instantiate()
-        customer_id = Customer.create().customer.id
-        client_token = json.loads(TestHelper.generate_decoded_client_token())
-        authorization_fingerprint = client_token["authorizationFingerprint"]
-        http = ClientApiHttp(config, {
-                    "authorization_fingerprint": authorization_fingerprint,
-                    "shared_customer_identifier": "fake_identifier",
-                    "shared_customer_identifier_type": "testing"
-                })
-        status_code, nonce = http.get_credit_card_nonce({
-            "number": "4000111111111115",
-            "expirationMonth": "11",
-            "expirationYear": "2099"
-        })
-        self.assertTrue(status_code == 201)
-
-        result = PaymentMethod.create({
-            "payment_method_nonce": nonce,
-            "customer_id": customer_id,
-            "options": {
-                "verify_card": "true",
-                "verification_merchant_account_id": TestHelper.non_default_merchant_account_id
-            }
-        })
-
-        self.assertFalse(result.is_success)
-        self.assertTrue(result.credit_card_verification.status == Transaction.Status.ProcessorDeclined)
-        self.assertTrue(result.credit_card_verification.processor_response_code == "2000")
-        self.assertTrue(result.credit_card_verification.processor_response_text == "Do Not Honor")
-        self.assertTrue(result.credit_card_verification.merchant_account_id == TestHelper.non_default_merchant_account_id)
-
-    def test_create_respects_fail_one_duplicate_payment_method_when_included_outside_of_the_nonce(self):
-        customer_id = Customer.create().customer.id
-        credit_card_result = CreditCard.create({
-            "customer_id": customer_id,
-            "number": CreditCardNumbers.Visa,
-            "expiration_date": "05/2012"
-        })
-        self.assertTrue(credit_card_result.is_success)
-
-        config = Configuration.instantiate()
-        customer_id = Customer.create().customer.id
-        client_token = json.loads(TestHelper.generate_decoded_client_token())
-        authorization_fingerprint = client_token["authorizationFingerprint"]
-        http = ClientApiHttp(config, {
-                    "authorization_fingerprint": authorization_fingerprint,
-                    "shared_customer_identifier": "fake_identifier",
-                    "shared_customer_identifier_type": "testing"
-                })
-        status_code, nonce = http.get_credit_card_nonce({
-            "number": CreditCardNumbers.Visa,
-            "expiration_date": "05/2012"
-        })
-        self.assertTrue(status_code == 201)
-
-        result = PaymentMethod.create({
-            "payment_method_nonce": nonce,
-            "customer_id": customer_id,
-            "options": {
-                "fail_on_duplicate_payment_method": "true"
-            }
-        })
-
-        self.assertFalse(result.is_success)
-        self.assertTrue(result.errors.deep_errors[0].code == "81724")
-
-
-    def test_create_allows_passing_billing_address_id_outside_the_nonce(self):
-        customer_id = Customer.create().customer.id
-        http = ClientApiHttp.create()
-        status_code, nonce = http.get_credit_card_nonce({
-            "number": "4111111111111111",
-            "expirationMonth": "12",
-            "expirationYear": "2020",
-            "options": {"validate": "false"}
-        })
-        self.assertTrue(status_code == 202)
-
-        address_result = Address.create({
-            "customer_id": customer_id,
-            "first_name": "Bobby",
-            "last_name": "Tables"
-        })
-        self.assertTrue(address_result.is_success)
-
-        payment_method_result = PaymentMethod.create({
-            "payment_method_nonce": nonce,
-            "customer_id": customer_id,
-            "billing_address_id": address_result.address.id
-        })
-
-        self.assertTrue(payment_method_result.is_success)
-        self.assertTrue(isinstance(payment_method_result.payment_method, CreditCard))
-        token = payment_method_result.payment_method.token
-
-        found_credit_card = CreditCard.find(token)
-        self.assertFalse(found_credit_card is None)
-        self.assertTrue(found_credit_card.billing_address.first_name == "Bobby")
-        self.assertTrue(found_credit_card.billing_address.last_name == "Tables")
-
-    def test_create_allows_passing_billing_address_outside_the_nonce(self):
-        customer_id = Customer.create().customer.id
-        http = ClientApiHttp.create()
-        status_code, nonce = http.get_credit_card_nonce({
-            "number": "4111111111111111",
-            "expirationMonth": "12",
-            "expirationYear": "2020",
-            "options": {"validate": "false"}
-        })
-        self.assertTrue(status_code == 202)
-
-        result = PaymentMethod.create({
-            "payment_method_nonce": nonce,
-            "customer_id": customer_id,
-            "billing_address": {
-                "street_address": "123 Abc Way"
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertTrue(isinstance(result.payment_method, CreditCard))
-        token = result.payment_method.token
-
-        found_credit_card = CreditCard.find(token)
-        self.assertFalse(found_credit_card is None)
-        self.assertTrue(found_credit_card.billing_address.street_address == "123 Abc Way")
-
-    def test_create_overrides_the_billing_address_in_the_nonce(self):
-        customer_id = Customer.create().customer.id
-        http = ClientApiHttp.create()
-        status_code, nonce = http.get_credit_card_nonce({
-            "number": "4111111111111111",
-            "expirationMonth": "12",
-            "expirationYear": "2020",
-            "options": {"validate": "false"},
-            "billing_address": {
-                "street_address": "456 Xyz Way"
-            }
-        })
-        self.assertTrue(status_code == 202)
-
-        result = PaymentMethod.create({
-            "payment_method_nonce": nonce,
-            "customer_id": customer_id,
-            "billing_address": {
-                "street_address": "123 Abc Way"
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertTrue(isinstance(result.payment_method, CreditCard))
-        token = result.payment_method.token
-
-        found_credit_card = CreditCard.find(token)
-        self.assertFalse(found_credit_card is None)
-        self.assertTrue(found_credit_card.billing_address.street_address == "123 Abc Way")
-
-    def test_create_does_not_override_the_billing_address_for_a_valuted_credit_card(self):
-        config = Configuration.instantiate()
-        customer_id = Customer.create().customer.id
-        client_token = json.loads(TestHelper.generate_decoded_client_token({"customer_id": customer_id}))
-        authorization_fingerprint = client_token["authorizationFingerprint"]
-        http = ClientApiHttp(config, {"authorization_fingerprint": authorization_fingerprint})
-        status_code, nonce = http.get_credit_card_nonce({
-            "number": "4111111111111111",
-            "expirationMonth": "12",
-            "expirationYear": "2020",
-            "billing_address": {
-                "street_address": "456 Xyz Way"
-            }
-        })
-        self.assertTrue(status_code == 201)
-
-        result = PaymentMethod.create({
-            "payment_method_nonce": nonce,
-            "customer_id": customer_id,
-            "billing_address": {
-                "street_address": "123 Abc Way"
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertTrue(isinstance(result.payment_method, CreditCard))
-        token = result.payment_method.token
-
-        found_credit_card = CreditCard.find(token)
-        self.assertFalse(found_credit_card is None)
-        self.assertTrue(found_credit_card.billing_address.street_address == "456 Xyz Way")
-
-    def test_create_does_not_return_an_error_if_credit_card_options_are_present_for_paypal_nonce(self):
-        customer_id = Customer.create().customer.id
-        original_token = "paypal-account-" + str(int(time.time()))
-        nonce = TestHelper.nonce_for_paypal_account({
-            "consent_code": "consent-code",
-            "token": original_token
-        })
-
-        result = PaymentMethod.create({
-            "payment_method_nonce": nonce,
-            "customer_id": customer_id,
-            "options": {
-                "verify_card": "true",
-                "fail_on_duplicate_payment_method": "true",
-                "verification_merchant_account_id": "not_a_real_merchant_account_id"
-            }
-        })
-
-        self.assertTrue(result.is_success)
-
-    def test_create_for_paypal_ignores_passed_billing_address_id(self):
-        nonce = TestHelper.nonce_for_paypal_account({
-            "consent_code": "consent-code"
-        })
-        customer_id = Customer.create().customer.id
-        result = PaymentMethod.create({
-            "payment_method_nonce": nonce,
-            "customer_id": customer_id,
-            "billing_address_id": "address_id"
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertTrue(isinstance(result.payment_method, PayPalAccount))
-        self.assertFalse(result.payment_method.image_url is None)
-        token = result.payment_method.token
-
-        found_paypal_account = PayPalAccount.find(token)
-        self.assertFalse(found_paypal_account is None)
-
-    def test_create_for_paypal_ignores_passed_billing_address_params(self):
-        nonce = TestHelper.nonce_for_paypal_account({
-            "consent_code": "PAYPAL_CONSENT_CODE"
-        })
-        customer_id = Customer.create().customer.id
-        result = PaymentMethod.create({
-            "payment_method_nonce": nonce,
-            "customer_id": customer_id,
-            "billing_address": {
-                "street_address": "123 Abc Way"
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertTrue(isinstance(result.payment_method, PayPalAccount))
-        self.assertFalse(result.payment_method.image_url is None)
-        token = result.payment_method.token
-
-        found_paypal_account = PayPalAccount.find(token)
-        self.assertFalse(found_paypal_account is None)
-
-    def test_create_payment_method_with_account_type_debit(self):
-        config = Configuration.instantiate()
-        customer_id = Customer.create().customer.id
-        client_token = json.loads(TestHelper.generate_decoded_client_token())
-        authorization_fingerprint = client_token["authorizationFingerprint"]
-        http = ClientApiHttp(config, {
-            "authorization_fingerprint": authorization_fingerprint,
-            "shared_customer_identifier": "fake_identifier",
-            "shared_customer_identifier_type": "testing",
-        })
-        status_code, nonce = http.get_credit_card_nonce({
-            "number": CreditCardNumbers.Hiper,
-            "expirationMonth": "11",
-            "expirationYear": "2099",
-        })
-        self.assertTrue(status_code == 201)
-
-        result = PaymentMethod.create({
-            "customer_id": customer_id,
-            "payment_method_nonce": nonce,
-            "options": {
-                "verify_card": "true",
-                "verification_merchant_account_id": TestHelper.hiper_brl_merchant_account_id,
-                "verification_amount": "1.02",
-                "verification_account_type": "debit",
-            },
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertEqual("debit", result.payment_method.verifications[0]["credit_card"]["account_type"])
-
-    def test_create_payment_method_with_account_type_credit(self):
-        config = Configuration.instantiate()
-        customer_id = Customer.create().customer.id
-        client_token = json.loads(TestHelper.generate_decoded_client_token())
-        authorization_fingerprint = client_token["authorizationFingerprint"]
-        http = ClientApiHttp(config, {
-            "authorization_fingerprint": authorization_fingerprint,
-            "shared_customer_identifier": "fake_identifier",
-            "shared_customer_identifier_type": "testing",
-        })
-        status_code, nonce = http.get_credit_card_nonce({
-            "number": CreditCardNumbers.Hiper,
-            "expirationMonth": "11",
-            "expirationYear": "2099",
-        })
-        self.assertTrue(status_code == 201)
-
-        result = PaymentMethod.create({
-            "customer_id": customer_id,
-            "payment_method_nonce": nonce,
-            "options": {
-                "verify_card": "true",
-                "verification_merchant_account_id": TestHelper.hiper_brl_merchant_account_id,
-                "verification_amount": "1.02",
-                "verification_account_type": "credit",
-            },
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertEqual("credit", result.payment_method.verifications[0]["credit_card"]["account_type"])
-
-    def test_create_credit_card_with_account_type_debit(self):
-        customer = Customer.create().customer
-        result = CreditCard.create({
-            "customer_id": customer.id,
-            "number": CreditCardNumbers.Hiper,
-            "expiration_date": "05/2009",
-            "cvv": "100",
-            "cardholder_name": "John Doe",
-            "options": {
-                "verification_merchant_account_id": TestHelper.hiper_brl_merchant_account_id,
-                "verification_account_type": "debit",
-                "verify_card": True,
-            }
-        })
-        self.assertTrue(result.is_success)
-        self.assertEqual("debit", result.credit_card.verifications[0]["credit_card"]["account_type"])
-
-    def test_create_credit_card_with_account_type_credit(self):
-        customer = Customer.create().customer
-        result = CreditCard.create({
-            "customer_id": customer.id,
-            "number": CreditCardNumbers.Hiper,
-            "expiration_date": "05/2009",
-            "cvv": "100",
-            "cardholder_name": "John Doe",
-            "options": {
-                "verification_merchant_account_id": TestHelper.hiper_brl_merchant_account_id,
-                "verification_account_type": "credit",
-                "verify_card": True,
-            }
-        })
-        self.assertTrue(result.is_success)
-        self.assertEqual("credit", result.credit_card.verifications[0]["credit_card"]["account_type"])
-
-    def test_create_with_usupported_merchant_account(self):
-        customer = Customer.create().customer
-        result = CreditCard.create({
-            "customer_id": customer.id,
-            "number": CreditCardNumbers.Visa,
-            "expiration_date": "05/2009",
-            "cvv": "100",
-            "cardholder_name": "John Doe",
-            "options": {
-                "verification_account_type": "debit",
-                "verify_card": True,
-            }
-        })
-        self.assertFalse(result.is_success)
-
-        errors = result.errors.for_object("credit_card").for_object("options").on("verification_account_type")
-
-        self.assertEqual(1, len(errors))
-        self.assertEqual(ErrorCodes.CreditCard.VerificationAccountTypeNotSupported, errors[0].code)
-
-    def test_create_with_invalid_account_type(self):
-        customer = Customer.create().customer
-        result = CreditCard.create({
-            "customer_id": customer.id,
-            "number": CreditCardNumbers.Hiper,
-            "expiration_date": "05/2009",
-            "cvv": "100",
-            "cardholder_name": "John Doe",
-            "options": {
-                "verification_merchant_account_id": TestHelper.hiper_brl_merchant_account_id,
-                "verification_account_type": "invalid",
-                "verify_card": True,
-            }
-        })
-        self.assertFalse(result.is_success)
-
-        errors = result.errors.for_object("credit_card").for_object("options").on("verification_account_type")
-
-        self.assertEqual(1, len(errors))
-        self.assertEqual(ErrorCodes.CreditCard.VerificationAccountTypeIsInvald, errors[0].code)
-
-    def test_find_returns_an_abstract_payment_method(self):
-        customer_id = Customer.create().customer.id
-        result = PaymentMethod.create({
-            "customer_id": customer_id,
-            "payment_method_nonce": Nonces.AbstractTransactable
-        })
-        self.assertTrue(result.is_success)
-
-        found_payment_method = PaymentMethod.find(result.payment_method.token)
-        self.assertNotEqual(None, found_payment_method)
-        self.assertEqual(found_payment_method.token, result.payment_method.token)
-
-    def test_find_returns_a_paypal_account(self):
-        customer_id = Customer.create().customer.id
-        result = PaymentMethod.create({
-            "customer_id": customer_id,
-            "payment_method_nonce": Nonces.PayPalFuturePayment
-        })
-        self.assertTrue(result.is_success)
-
-        found_account = PaymentMethod.find(result.payment_method.token)
-        self.assertNotEqual(None, found_account)
-        self.assertEqual(PayPalAccount, found_account.__class__)
-        self.assertEqual("jane.doe@example.com", found_account.email)
-
-    def test_find_returns_a_credit_card(self):
-        customer = Customer.create().customer
-        result = CreditCard.create({
-            "customer_id": customer.id,
-            "number": "4111111111111111",
-            "expiration_date": "05/2009",
-            "cvv": "100",
-            "cardholder_name": "John Doe"
-        })
-        self.assertTrue(result.is_success)
-
-        found_credit_card = PaymentMethod.find(result.credit_card.token)
-        self.assertNotEqual(None, found_credit_card)
-        self.assertEqual(CreditCard, found_credit_card.__class__)
-        self.assertEqual("411111", found_credit_card.bin)
-
-    def test_find_returns_an_android_pay_card(self):
-        customer = Customer.create().customer
-        result = PaymentMethod.create({
-            "customer_id": customer.id,
-            "payment_method_nonce": Nonces.AndroidPayCard
-        })
-
-        self.assertTrue(result.is_success)
-        android_pay_card = result.payment_method
-
-        found_android_pay_card = PaymentMethod.find(android_pay_card.token)
-        self.assertNotEqual(None, found_android_pay_card)
-        self.assertEqual(AndroidPayCard, found_android_pay_card.__class__)
-        self.assertEqual(found_android_pay_card.token, android_pay_card.token)
-
-    def test_delete_deletes_a_credit_card(self):
-        customer = Customer.create().customer
-        result = CreditCard.create({
-            "customer_id": customer.id,
-            "number": "4111111111111111",
-            "expiration_date": "05/2009",
-            "cvv": "100",
-            "cardholder_name": "John Doe"
-        })
-        self.assertTrue(result.is_success)
-
-        delete_result = PaymentMethod.delete(result.credit_card.token)
-        self.assertTrue(delete_result.is_success)
-        self.assertRaises(NotFoundError, PaymentMethod.find, result.credit_card.token)
-
-    def test_delete_deletes_a_paypal_account(self):
-        customer_id = Customer.create().customer.id
-        result = PaymentMethod.create({
-            "customer_id": customer_id,
-            "payment_method_nonce": Nonces.PayPalFuturePayment
-        })
-        self.assertTrue(result.is_success)
-
-        delete_result = PaymentMethod.delete(result.payment_method.token, {"revoke_all_grants": False})
-        self.assertTrue(delete_result.is_success)
-        self.assertRaises(NotFoundError, PaymentMethod.find, result.payment_method.token)
-
-    def test_update_credit_cards_updates_the_credit_card(self):
-        customer_id = Customer.create().customer.id
-        credit_card_result = CreditCard.create({
-            "cardholder_name": "Original Holder",
-            "customer_id": customer_id,
-            "cvv": "123",
-            "number": CreditCardNumbers.Visa,
-            "expiration_date": "05/2012"
-        })
-        update_result = PaymentMethod.update(credit_card_result.credit_card.token, {
-            "cardholder_name": "New Holder",
-            "cvv": "456",
-            "number": CreditCardNumbers.MasterCard,
-            "expiration_date": "06/2013"
-        })
-        self.assertTrue(update_result.is_success)
-        self.assertTrue(update_result.payment_method.token == credit_card_result.credit_card.token)
-        updated_credit_card = update_result.payment_method
-        self.assertTrue(updated_credit_card.bin == CreditCardNumbers.MasterCard[:6])
-        self.assertTrue(updated_credit_card.last_4 == CreditCardNumbers.MasterCard[-4:])
-        self.assertTrue(updated_credit_card.expiration_date == "06/2013")
-
-    def test_update_credit_cards_with_account_type_credit(self):
-        customer = Customer.create().customer
-        result = CreditCard.create({
-            "customer_id": customer.id,
-            "number": CreditCardNumbers.Visa,
-            "expiration_date": "05/2009",
-            "cvv": "100",
-            "cardholder_name": "John Doe",
-        })
-        update_result = PaymentMethod.update(result.credit_card.token, {
-            "cardholder_name": "New Holder",
-            "cvv": "456",
-            "number": CreditCardNumbers.Hiper,
-            "expiration_date": "06/2013",
-            "options": {
-                "verification_merchant_account_id": TestHelper.hiper_brl_merchant_account_id,
-                "verification_account_type": "credit",
-                "verify_card": True,
-            }
-        })
-        self.assertTrue(update_result.is_success)
-        self.assertEqual("credit", update_result.payment_method.verification.credit_card["account_type"])
-
-    def test_update_credit_cards_with_account_type_debit(self):
-        customer = Customer.create().customer
-        result = CreditCard.create({
-            "customer_id": customer.id,
-            "number": CreditCardNumbers.Visa,
-            "expiration_date": "05/2009",
-            "cvv": "100",
-            "cardholder_name": "John Doe",
-        })
-        update_result = PaymentMethod.update(result.credit_card.token, {
-            "cardholder_name": "New Holder",
-            "cvv": "456",
-            "number": CreditCardNumbers.Hiper,
-            "expiration_date": "06/2013",
-            "options": {
-                "verification_merchant_account_id": TestHelper.hiper_brl_merchant_account_id,
-                "verification_account_type": "debit",
-                "verify_card": True,
-            }
-        })
-        self.assertTrue(update_result.is_success)
-        self.assertEqual("debit", update_result.payment_method.verification.credit_card["account_type"])
-
-    def test_update_credit_cards_with_invalid_account_type(self):
-        customer = Customer.create().customer
-        result = CreditCard.create({
-            "customer_id": customer.id,
-            "number": CreditCardNumbers.Visa,
-            "expiration_date": "05/2009",
-            "cvv": "100",
-            "cardholder_name": "John Doe",
-        })
-        update_result = PaymentMethod.update(result.credit_card.token, {
-            "cardholder_name": "New Holder",
-            "cvv": "456",
-            "number": CreditCardNumbers.Hiper,
-            "expiration_date": "06/2013",
-            "options": {
-                "verification_merchant_account_id": TestHelper.hiper_brl_merchant_account_id,
-                "verification_account_type": "invalid",
-                "verify_card": True,
-            }
-        })
-        self.assertFalse(update_result.is_success)
-        errors = update_result.errors.for_object("credit_card").for_object("options").on("verification_account_type")
-        self.assertEqual(1, len(errors))
-        self.assertEqual(ErrorCodes.CreditCard.VerificationAccountTypeIsInvald, errors[0].code)
-
-    def test_update_credit_cards_with_unsupported_merchant_account(self):
-        customer = Customer.create().customer
-        result = CreditCard.create({
-            "customer_id": customer.id,
-            "number": CreditCardNumbers.Visa,
-            "expiration_date": "05/2009",
-            "cvv": "100",
-            "cardholder_name": "John Doe",
-        })
-        update_result = PaymentMethod.update(result.credit_card.token, {
-            "cardholder_name": "New Holder",
-            "cvv": "456",
-            "number": CreditCardNumbers.Visa,
-            "expiration_date": "06/2013",
-            "options": {
-                "verification_account_type": "debit",
-                "verify_card": True,
-            }
-        })
-        self.assertFalse(update_result.is_success)
-        errors = update_result.errors.for_object("credit_card").for_object("options").on("verification_account_type")
-        self.assertEqual(1, len(errors))
-        self.assertEqual(ErrorCodes.CreditCard.VerificationAccountTypeNotSupported, errors[0].code)
-
-    def test_update_creates_a_new_billing_address_by_default(self):
-        customer_id = Customer.create().customer.id
-        credit_card_result = CreditCard.create({
-            "customer_id": customer_id,
-            "number": CreditCardNumbers.Visa,
-            "expiration_date": "05/2012",
-            "billing_address": {
-                "street_address": "123 Nigeria Ave"
-            }
-        })
-        update_result = PaymentMethod.update(credit_card_result.credit_card.token, {
-            "billing_address": {
-                "region": "IL"
-            }
-        })
-        self.assertTrue(update_result.is_success)
-        updated_credit_card = update_result.payment_method
-        self.assertTrue(updated_credit_card.billing_address.region == "IL")
-        self.assertTrue(updated_credit_card.billing_address.street_address is None)
-        self.assertFalse(updated_credit_card.billing_address.id == credit_card_result.credit_card.billing_address.id)
-
-    def test_update_updates_the_billing_address_if_option_is_specified(self):
-        customer_id = Customer.create().customer.id
-        credit_card_result = CreditCard.create({
-            "customer_id": customer_id,
-            "number": CreditCardNumbers.Visa,
-            "expiration_date": "05/2012",
-            "billing_address": {
-                "street_address": "123 Nigeria Ave"
-            }
-        })
-        update_result = PaymentMethod.update(credit_card_result.credit_card.token, {
-            "billing_address": {
-                "region": "IL",
-                "options": {
-                    "update_existing": "true"
-                }
-            }
-        })
-        self.assertTrue(update_result.is_success)
-        updated_credit_card = update_result.payment_method
-        self.assertTrue(updated_credit_card.billing_address.region == "IL")
-        self.assertTrue(updated_credit_card.billing_address.street_address == "123 Nigeria Ave")
-        self.assertTrue(updated_credit_card.billing_address.id == credit_card_result.credit_card.billing_address.id)
-
-    def test_update_updates_the_country_via_codes(self):
-        customer_id = Customer.create().customer.id
-        credit_card_result = CreditCard.create({
-            "customer_id": customer_id,
-            "number": CreditCardNumbers.Visa,
-            "expiration_date": "05/2012",
-            "billing_address": {
-                "street_address": "123 Nigeria Ave"
-            }
-        })
-        update_result = PaymentMethod.update(credit_card_result.credit_card.token, {
-            "billing_address": {
-                "country_name": "American Samoa",
-                "country_code_alpha2": "AS",
-                "country_code_alpha3": "ASM",
-                "country_code_numeric": "016",
-                "options": {
-                    "update_existing": "true"
-                }
-            }
-        })
-        self.assertTrue(update_result.is_success)
-        updated_credit_card = update_result.payment_method
-        self.assertTrue(updated_credit_card.billing_address.country_name == "American Samoa")
-        self.assertTrue(updated_credit_card.billing_address.country_code_alpha2 == "AS")
-        self.assertTrue(updated_credit_card.billing_address.country_code_alpha3 == "ASM")
-        self.assertTrue(updated_credit_card.billing_address.country_code_numeric == "016")
-
-    def test_update_can_pass_expiration_month_and_expiration_year(self):
-        customer_id = Customer.create().customer.id
-        credit_card_result = CreditCard.create({
-            "customer_id": customer_id,
-            "number": CreditCardNumbers.Visa,
-            "expiration_date": "05/2012"
-        })
-        update_result = PaymentMethod.update(credit_card_result.credit_card.token, {
-            "number": CreditCardNumbers.MasterCard,
-            "expiration_month": "07",
-            "expiration_year": "2011"
-        })
-        self.assertTrue(update_result.is_success)
-        self.assertTrue(update_result.payment_method.token == credit_card_result.credit_card.token)
-        updated_credit_card = update_result.payment_method
-        self.assertTrue(updated_credit_card.expiration_month == "07")
-        self.assertTrue(updated_credit_card.expiration_year == "2011")
-        self.assertTrue(updated_credit_card.expiration_date == "07/2011")
-
-    def test_update_verifies_the_update_if_options_verify_card_is_true(self):
-        customer_id = Customer.create().customer.id
-        credit_card_result = CreditCard.create({
-            "cardholder_name": "Original Holder",
-            "customer_id": customer_id,
-            "cvv": "123",
-            "number": CreditCardNumbers.Visa,
-            "expiration_date": "05/2012"
-        })
-        update_result = PaymentMethod.update(credit_card_result.credit_card.token, {
-            "cardholder_name": "New Holder",
-            "cvv": "456",
-            "number": CreditCardNumbers.FailsSandboxVerification.MasterCard,
-            "expiration_date": "06/2013",
-            "options": {
-                "verify_card": "true"
-            }
-        })
-        self.assertFalse(update_result.is_success)
-        self.assertTrue(update_result.credit_card_verification.status == CreditCardVerification.Status.ProcessorDeclined)
-        self.assertTrue(update_result.credit_card_verification.gateway_rejection_reason is None)
-
-    def test_update_can_pass_custom_verification_amount(self):
-        customer_id = Customer.create().customer.id
-        credit_card_result = CreditCard.create({
-            "cardholder_name": "Card Holder",
-            "customer_id": customer_id,
-            "cvv": "123",
-            "number": CreditCardNumbers.Visa,
-            "expiration_date": "05/2020"
-        })
-        update_result = PaymentMethod.update(credit_card_result.credit_card.token, {
-            "payment_method_nonce": Nonces.ProcessorDeclinedMasterCard,
-            "options": {
-                "verify_card": "true",
-                "verification_amount": "2.34"
-            }
-        })
-        self.assertFalse(update_result.is_success)
-        self.assertTrue(update_result.credit_card_verification.status == CreditCardVerification.Status.ProcessorDeclined)
-        self.assertTrue(update_result.credit_card_verification.gateway_rejection_reason is None)
-
-    def test_update_can_update_the_billing_address(self):
-        customer_id = Customer.create().customer.id
-        credit_card_result = CreditCard.create({
-            "cardholder_name": "Original Holder",
-            "customer_id": customer_id,
-            "cvv": "123",
-            "number": CreditCardNumbers.Visa,
-            "expiration_date": "05/2012",
-            "billing_address": {
-                "first_name": "Old First Name",
-                "last_name": "Old Last Name",
-                "company": "Old Company",
-                "street_address": "123 Old St",
-                "extended_address": "Apt Old",
-                "locality": "Old City",
-                "region": "Old State",
-                "postal_code": "12345",
-                "country_name": "Canada"
-            }
-        })
-        update_result = PaymentMethod.update(credit_card_result.credit_card.token, {
-            "options": {"verify_card": "false"},
-            "billing_address": {
-                "first_name": "New First Name",
-                "last_name": "New Last Name",
-                "company": "New Company",
-                "street_address": "123 New St",
-                "extended_address": "Apt New",
-                "locality": "New City",
-                "region": "New State",
-                "postal_code": "56789",
-                "country_name": "United States of America"
-            }
-        })
-        self.assertTrue(update_result.is_success)
-        address = update_result.payment_method.billing_address
-        self.assertTrue(address.first_name == "New First Name")
-        self.assertTrue(address.last_name == "New Last Name")
-        self.assertTrue(address.company == "New Company")
-        self.assertTrue(address.street_address == "123 New St")
-        self.assertTrue(address.extended_address == "Apt New")
-        self.assertTrue(address.locality == "New City")
-        self.assertTrue(address.region == "New State")
-        self.assertTrue(address.postal_code == "56789")
-        self.assertTrue(address.country_name == "United States of America")
-
-    def test_update_returns_an_error_response_if_invalid(self):
-        customer_id = Customer.create().customer.id
-        credit_card_result = CreditCard.create({
-            "cardholder_name": "Original Holder",
-            "customer_id": customer_id,
-            "number": CreditCardNumbers.Visa,
-            "expiration_date": "05/2012"
-        })
-        update_result = PaymentMethod.update(credit_card_result.credit_card.token, {
-            "cardholder_name": "New Holder",
-            "number": "invalid",
-            "expiration_date": "05/2014",
-        })
-        self.assertFalse(update_result.is_success)
-
-        number_errors = update_result.errors.for_object("credit_card").on("number")
-        self.assertEqual(1, len(number_errors))
-        self.assertEqual("Credit card number must be 12-19 digits.", number_errors[0].message)
-
-    def test_update_can_update_the_default(self):
-        customer_id = Customer.create().customer.id
-        card1 = CreditCard.create({
-            "customer_id": customer_id,
-            "number": CreditCardNumbers.Visa,
-            "expiration_date": "05/2009"
-        }).credit_card
-        card2 = CreditCard.create({
-            "customer_id": customer_id,
-            "number": CreditCardNumbers.Visa,
-            "expiration_date": "05/2009"
-        }).credit_card
-
-        self.assertTrue(card1.default)
-        self.assertFalse(card2.default)
-
-        PaymentMethod.update(card2.token, {
-            "options": {"make_default": True}
-        })
-
-        self.assertFalse(CreditCard.find(card1.token).default)
-        self.assertTrue(CreditCard.find(card2.token).default)
-
-    def test_update_updates_a_paypal_accounts_token(self):
-        customer_id = Customer.create().customer.id
-        original_token = "paypal-account-" + str(int(time.time()))
-        nonce = TestHelper.nonce_for_paypal_account({
-            "consent_code": "consent-code",
-            "token": original_token
-        })
-        original_result = PaymentMethod.create({
-             "payment_method_nonce": nonce,
-             "customer_id": customer_id
-        })
-
-        updated_token = "UPDATED_TOKEN-" + str(random.randint(0, 100000000))
-        PaymentMethod.update(
-            original_token,
-            {"token": updated_token}
-        )
-
-        updated_paypal_account = PayPalAccount.find(updated_token)
-        self.assertTrue(updated_paypal_account.email == original_result.payment_method.email)
-        self.assertRaises(NotFoundError, PaymentMethod.find, original_token)
-
-    def test_update_can_make_a_paypal_account_the_default_payment_method(self):
-        customer_id = Customer.create().customer.id
-        credit_card_result = CreditCard.create({
-            "customer_id": customer_id,
-            "number": CreditCardNumbers.Visa,
-            "expiration_date": "05/2009",
-            "options": {"make_default": "true"}
-        })
-        self.assertTrue(credit_card_result.is_success)
-
-        nonce = TestHelper.nonce_for_paypal_account({
-            "consent_code": "consent-code"
-        })
-        original_token = PaymentMethod.create({
-             "payment_method_nonce": nonce,
-             "customer_id": customer_id
-        }).payment_method.token
-
-        PaymentMethod.update(
-            original_token,
-            {"options": {"make_default": "true"}}
-        )
-
-        updated_paypal_account = PayPalAccount.find(original_token)
-        self.assertTrue(updated_paypal_account.default)
-
-    def test_update_fails_to_updates_a_paypal_accounts_token_with(self):
-        customer_id = Customer.create().customer.id
-        first_token = "paypal-account-" + str(random.randint(0, 100000000))
-        second_token = "paypal-account-" + str(random.randint(0, 100000000))
-
-        first_nonce = TestHelper.nonce_for_paypal_account({
-            "consent_code": "consent-code",
-            "token": first_token
-        })
-        PaymentMethod.create({
-             "payment_method_nonce": first_nonce,
-             "customer_id": customer_id
-        })
-
-        second_nonce = TestHelper.nonce_for_paypal_account({
-            "consent_code": "consent-code",
-            "token": second_token
-        })
-        PaymentMethod.create({
-             "payment_method_nonce": second_nonce,
-             "customer_id": customer_id
-        })
-
-        updated_result = PaymentMethod.update(
-            first_token,
-            {"token": second_token}
-        )
-
-        self.assertFalse(updated_result.is_success)
-
-        errors = updated_result.errors.deep_errors
-        self.assertEqual(1, len(errors))
-        self.assertEqual("92906", errors[0].code)
-
-    def test_payment_method_grant_raises_on_non_existent_tokens(self):
-        granting_gateway, _ = TestHelper.create_payment_method_grant_fixtures()
-        self.assertRaises(NotFoundError, granting_gateway.payment_method.grant, "non-existant-token", False)
-
-    def test_payment_method_grant_returns_one_time_nonce(self):
-        """
-        Payment method grant returns a nonce that is transactable by a partner merchant exactly once
-        """
-        granting_gateway, credit_card = TestHelper.create_payment_method_grant_fixtures()
-        grant_result = granting_gateway.payment_method.grant(credit_card.token, { "allow_vaulting": False });
-        self.assertTrue(grant_result.is_success)
-
-        result = Transaction.sale({
-            "payment_method_nonce": grant_result.payment_method_nonce.nonce,
-            "amount": TransactionAmounts.Authorize
-        })
-        self.assertTrue(result.is_success)
-        result = Transaction.sale({
-            "payment_method_nonce": grant_result.payment_method_nonce.nonce,
-            "amount": TransactionAmounts.Authorize
-        })
-        self.assertFalse(result.is_success)
-
-    def test_payment_method_grant_returns_a_nonce_that_is_not_vaultable(self):
-        granting_gateway, credit_card = TestHelper.create_payment_method_grant_fixtures()
-        grant_result = granting_gateway.payment_method.grant(credit_card.token, False)
-        customer_id = Customer.create().customer.id
-
-        result = PaymentMethod.create({
-            "customer_id": customer_id,
-            "payment_method_nonce": grant_result.payment_method_nonce.nonce
-        })
-        self.assertFalse(result.is_success)
-
-    def test_payment_method_grant_returns_a_nonce_that_is_vaultable(self):
-        granting_gateway, credit_card = TestHelper.create_payment_method_grant_fixtures()
-        grant_result = granting_gateway.payment_method.grant(credit_card.token, { "allow_vaulting": True })
-        customer_id = Customer.create().customer.id
-
-        result = PaymentMethod.create({
-            "customer_id": customer_id,
-            "payment_method_nonce": grant_result.payment_method_nonce.nonce
-        })
-        self.assertTrue(result.is_success)
-
-    def test_payment_method_revoke_renders_a_granted_nonce_unusable(self):
-        granting_gateway, credit_card = TestHelper.create_payment_method_grant_fixtures()
-        grant_result = granting_gateway.payment_method.grant(credit_card.token)
-
-        revoke_result = granting_gateway.payment_method.revoke(credit_card.token)
-        self.assertTrue(revoke_result.is_success)
-
-        result = Transaction.sale({
-            "payment_method_nonce": grant_result.payment_method_nonce.nonce,
-            "amount": TransactionAmounts.Authorize
-        })
-        self.assertFalse(result.is_success)
-
-    def test_payment_method_revoke_raises_on_non_existent_tokens(self):
-        granting_gateway, _ = TestHelper.create_payment_method_grant_fixtures()
-        self.assertRaises(NotFoundError, granting_gateway.payment_method.revoke, "non-existant-token")
-
-class CreditCardForwardingTest(unittest.TestCase):
-    def setUp(self):
-        braintree.Configuration.configure(
-            braintree.Environment.Development,
-            "forward_payment_method_merchant_id",
-            "forward_payment_method_public_key",
-            "forward_payment_method_private_key"
-        )
-
-    def tearDown(self):
-        braintree.Configuration.configure(
-            braintree.Environment.Development,
-            "integration_merchant_id",
-            "integration_public_key",
-            "integration_private_key"
-        )
-
-    def test_forward_raises_exception(self):
-        customer = Customer.create().customer
-        credit_card_result = CreditCard.create({
-            "customer_id": customer.id,
-            "number": "4111111111111111",
-            "expiration_date": "05/2025"
-        })
-        self.assertTrue(credit_card_result.is_success)
-        source_merchant_card = credit_card_result.credit_card
-
-        self.assertRaises(NotFoundError, CreditCard.forward, source_merchant_card.token, "integration_merchant_id")
-
-    def test_forward_invalid_token_raises_exception(self):
-        self.assertRaises(NotFoundError, CreditCard.forward, "invalid", "integration_merchant_id")
-
-    def test_forward_invalid_receiving_merchant_raises_exception(self):
-        customer = Customer.create().customer
-        credit_card_result = CreditCard.create({
-            "customer_id": customer.id,
-            "number": "4111111111111111",
-            "expiration_date": "05/2025"
-        })
-        self.assertTrue(credit_card_result.is_success)
-        source_merchant_card = credit_card_result.credit_card
-
-        self.assertRaises(NotFoundError, CreditCard.forward, source_merchant_card.token, "invalid_merchant_id")
diff --git a/tests/integration/test_payment_method_nonce.py b/tests/integration/test_payment_method_nonce.py
deleted file mode 100644
index 1f93199..0000000
--- a/tests/integration/test_payment_method_nonce.py
+++ /dev/null
@@ -1,152 +0,0 @@
-from tests.test_helper import *
-
-class TestPaymentMethodNonce(unittest.TestCase):
-    def test_create_nonce_from_payment_method(self):
-        customer_id = Customer.create().customer.id
-        credit_card_result = CreditCard.create({
-            "customer_id": customer_id,
-            "number": "4111111111111111",
-            "expiration_date": "05/2014",
-        })
-
-        result = PaymentMethodNonce.create(credit_card_result.credit_card.token)
-
-        self.assertTrue(result.is_success)
-        self.assertNotEqual(None, result.payment_method_nonce)
-        self.assertNotEqual(None, result.payment_method_nonce.nonce)
-
-    def test_create_raises_not_found_when_404(self):
-        self.assertRaises(NotFoundError, PaymentMethodNonce.create, "not-a-token")
-
-    def test_find_nonce_shows_details(self):
-        config = Configuration(
-            environment=Environment.Development,
-            merchant_id="integration_merchant_id",
-            public_key="integration_public_key",
-            private_key="integration_private_key"
-        )
-        gateway = BraintreeGateway(config)
-
-        nonce = PaymentMethodNonce.find("fake-valid-visa-nonce")
-
-        self.assertEqual("401288", nonce.details["bin"])
-
-    def test_find_nonce_shows_3ds_details(self):
-        config = Configuration(
-            environment=Environment.Development,
-            merchant_id="integration_merchant_id",
-            public_key="integration_public_key",
-            private_key="integration_private_key"
-        )
-        gateway = BraintreeGateway(config)
-
-        credit_card = {
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_month": "12",
-                "expiration_year": "2020"
-            }
-        }
-
-        nonce = TestHelper.generate_three_d_secure_nonce(gateway, credit_card)
-        found_nonce = PaymentMethodNonce.find(nonce)
-        three_d_secure_info = found_nonce.three_d_secure_info
-
-        self.assertEqual("CreditCard", found_nonce.type)
-        self.assertEqual(nonce, found_nonce.nonce)
-        self.assertEqual("Y", three_d_secure_info.enrolled)
-        self.assertEqual("authenticate_successful", three_d_secure_info.status)
-        self.assertEqual(True, three_d_secure_info.liability_shifted)
-        self.assertEqual(True, three_d_secure_info.liability_shift_possible)
-        self.assertEqual("test_cavv", three_d_secure_info.cavv)
-        self.assertEqual("test_xid", three_d_secure_info.xid)
-        self.assertEqual("test_eci", three_d_secure_info.eci_flag)
-        self.assertEqual("1.0.2", three_d_secure_info.three_d_secure_version)
-
-    def test_find_nonce_shows_paypal_details(self):
-        found_nonce = PaymentMethodNonce.find("fake-google-pay-paypal-nonce")
-
-        self.assertNotEqual(None, found_nonce.details["payer_info"]["first_name"])
-        self.assertNotEqual(None, found_nonce.details["payer_info"]["last_name"])
-        self.assertNotEqual(None, found_nonce.details["payer_info"]["email"])
-        self.assertNotEqual(None, found_nonce.details["payer_info"]["payer_id"])
-
-    def test_find_nonce_shows_venmo_details(self):
-        found_nonce = PaymentMethodNonce.find("fake-venmo-account-nonce")
-
-        self.assertEquals("99", found_nonce.details["last_two"])
-        self.assertEquals("venmojoe", found_nonce.details["username"])
-        self.assertEquals("Venmo-Joe-1", found_nonce.details["venmo_user_id"])
-
-    def test_exposes_null_3ds_info_if_none_exists(self):
-        http = ClientApiHttp.create()
-
-        _, nonce = http.get_paypal_nonce({
-            "consent-code": "consent-code",
-            "access-token": "access-token",
-            "options": {"validate": False}
-        })
-
-        found_nonce = PaymentMethodNonce.find(nonce)
-
-        self.assertEqual(nonce, found_nonce.nonce)
-        self.assertEqual(None, found_nonce.three_d_secure_info)
-
-    def test_find_raises_not_found_when_404(self):
-        self.assertRaises(NotFoundError, PaymentMethodNonce.find, "not-a-nonce")
-
-    def test_bin_data_has_commercial(self):
-        found_nonce = PaymentMethodNonce.find("fake-valid-commercial-nonce")
-        bin_data = found_nonce.bin_data
-
-        self.assertEqual(CreditCard.Commercial.Yes, bin_data.commercial)
-
-    def test_bin_data_has_country_of_issuance(self):
-        found_nonce = PaymentMethodNonce.find("fake-valid-country-of-issuance-cad-nonce")
-        bin_data = found_nonce.bin_data
-
-        self.assertEqual("CAN", bin_data.country_of_issuance)
-
-    def test_bin_data_debit(self):
-        found_nonce = PaymentMethodNonce.find("fake-valid-debit-nonce")
-        bin_data = found_nonce.bin_data
-
-        self.assertEqual(CreditCard.Debit.Yes, bin_data.debit)
-
-    def test_bin_data_durbin_regulated(self):
-        found_nonce = PaymentMethodNonce.find("fake-valid-durbin-regulated-nonce")
-        bin_data = found_nonce.bin_data
-
-        self.assertEqual(CreditCard.DurbinRegulated.Yes, bin_data.durbin_regulated)
-
-    def test_bin_data_issuing_bank(self):
-        found_nonce = PaymentMethodNonce.find("fake-valid-issuing-bank-network-only-nonce")
-        bin_data = found_nonce.bin_data
-
-        self.assertEqual("NETWORK ONLY", bin_data.issuing_bank)
-
-    def test_bin_data_payroll(self):
-        found_nonce = PaymentMethodNonce.find("fake-valid-payroll-nonce")
-        bin_data = found_nonce.bin_data
-
-        self.assertEqual(CreditCard.Payroll.Yes, bin_data.payroll)
-
-    def test_bin_data_prepaid(self):
-        found_nonce = PaymentMethodNonce.find("fake-valid-prepaid-nonce")
-        bin_data = found_nonce.bin_data
-
-        self.assertEqual(CreditCard.Prepaid.Yes, bin_data.prepaid)
-
-    def test_bin_data_unknown_values(self):
-        found_nonce = PaymentMethodNonce.find("fake-valid-unknown-indicators-nonce")
-        bin_data = found_nonce.bin_data
-
-        self.assertEqual(CreditCard.Commercial.Unknown, bin_data.commercial)
-        self.assertEqual(CreditCard.CountryOfIssuance.Unknown, bin_data.country_of_issuance)
-        self.assertEqual(CreditCard.Debit.Unknown, bin_data.debit)
-        self.assertEqual(CreditCard.DurbinRegulated.Unknown, bin_data.durbin_regulated)
-        self.assertEqual(CreditCard.Healthcare.Unknown, bin_data.healthcare)
-        self.assertEqual(CreditCard.IssuingBank.Unknown, bin_data.issuing_bank)
-        self.assertEqual(CreditCard.Payroll.Unknown, bin_data.payroll)
-        self.assertEqual(CreditCard.Prepaid.Unknown, bin_data.prepaid)
-        self.assertEqual(CreditCard.ProductId.Unknown, bin_data.product_id)
diff --git a/tests/integration/test_payment_method_us_bank_account.py b/tests/integration/test_payment_method_us_bank_account.py
deleted file mode 100644
index ef499aa..0000000
--- a/tests/integration/test_payment_method_us_bank_account.py
+++ /dev/null
@@ -1,230 +0,0 @@
-from tests.test_helper import *
-from braintree.us_bank_account_verification import UsBankAccountVerification
-
-class PaymentMethodWithUsBankAccountTest(unittest.TestCase):
-    def test_create_with_nonce(self):
-        customer_id = Customer.create().customer.id
-        result = PaymentMethod.create({
-            "customer_id": customer_id,
-            "payment_method_nonce": TestHelper.generate_valid_us_bank_account_nonce(),
-            "options": {
-                "verification_merchant_account_id": TestHelper.us_bank_merchant_account_id
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        us_bank_account = result.payment_method
-        self.assertIsInstance(us_bank_account, UsBankAccount)
-        self.assertEqual(us_bank_account.routing_number, "021000021")
-        self.assertEqual(us_bank_account.last_4, "1234")
-        self.assertEqual(us_bank_account.account_type, "checking")
-        self.assertEqual(us_bank_account.account_holder_name, "Dan Schulman")
-        self.assertTrue(re.match(r".*CHASE.*", us_bank_account.bank_name))
-        self.assertEqual(us_bank_account.default, True)
-        self.assertEqual(us_bank_account.ach_mandate.text, "cl mandate text")
-        self.assertIsInstance(us_bank_account.ach_mandate.accepted_at, datetime)
-        self.assertEqual(us_bank_account.verified, True)
-
-        self.assertEqual(len(us_bank_account.verifications), 1)
-
-        verification = us_bank_account.verifications[0]
-
-        self.assertEqual(verification.status, "verified")
-        self.assertEqual(verification.verification_method, UsBankAccountVerification.VerificationMethod.IndependentCheck)
-
-    def test_create_with_verification(self):
-        customer_id = Customer.create().customer.id
-        result = PaymentMethod.create({
-            "customer_id": customer_id,
-            "payment_method_nonce": TestHelper.generate_valid_us_bank_account_nonce("021000021", "1000000000"),
-            "options": {
-                "verification_merchant_account_id": TestHelper.us_bank_merchant_account_id,
-                "us_bank_account_verification_method": UsBankAccountVerification.VerificationMethod.NetworkCheck,
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        us_bank_account = result.payment_method
-        self.assertIsInstance(us_bank_account, UsBankAccount)
-        self.assertEqual(us_bank_account.routing_number, "021000021")
-        self.assertEqual(us_bank_account.last_4, "0000")
-        self.assertEqual(us_bank_account.account_type, "checking")
-        self.assertEqual(us_bank_account.account_holder_name, "Dan Schulman")
-        self.assertTrue(re.match(r".*CHASE.*", us_bank_account.bank_name))
-        self.assertEqual(us_bank_account.default, True)
-        self.assertEqual(us_bank_account.ach_mandate.text, "cl mandate text")
-        self.assertIsInstance(us_bank_account.ach_mandate.accepted_at, datetime)
-        self.assertEqual(us_bank_account.verified, True)
-
-        self.assertEqual(len(us_bank_account.verifications), 1)
-
-        verification = us_bank_account.verifications[0]
-
-        self.assertEqual(verification.status, UsBankAccountVerification.Status.Verified)
-        self.assertEqual(verification.verification_method, UsBankAccountVerification.VerificationMethod.NetworkCheck)
-
-    def test_create_fails_with_invalid_us_bank_account_nonce(self):
-        customer_id = Customer.create().customer.id
-        result = PaymentMethod.create({
-            "customer_id": customer_id,
-            "payment_method_nonce": TestHelper.generate_invalid_us_bank_account_nonce(),
-            "options": {
-                "verification_merchant_account_id": TestHelper.us_bank_merchant_account_id
-            }
-        })
-
-        self.assertFalse(result.is_success)
-        error_code = result.errors.for_object("payment_method").on("payment_method_nonce")[0].code
-        self.assertEqual(ErrorCodes.PaymentMethod.PaymentMethodNonceUnknown, error_code)
-
-    def test_update_payment_method_with_verification(self):
-        customer_id = Customer.create().customer.id
-        result = PaymentMethod.create({
-            "customer_id": customer_id,
-            "payment_method_nonce": TestHelper.generate_valid_us_bank_account_nonce("021000021", "1000000000"),
-            "options": {
-                "verification_merchant_account_id": TestHelper.us_bank_merchant_account_id,
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        us_bank_account = result.payment_method
-
-        self.assertEqual(len(us_bank_account.verifications), 1)
-
-        verification = us_bank_account.verifications[0]
-
-        self.assertEqual(verification.status, UsBankAccountVerification.Status.Verified)
-        self.assertEqual(verification.verification_method, UsBankAccountVerification.VerificationMethod.IndependentCheck)
-
-        result = PaymentMethod.update(us_bank_account.token, {
-            "options": {
-                "verification_merchant_account_id": TestHelper.us_bank_merchant_account_id,
-                "us_bank_account_verification_method": UsBankAccountVerification.VerificationMethod.NetworkCheck,
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        us_bank_account = result.payment_method
-
-        self.assertEqual(len(us_bank_account.verifications), 2)
-
-class PaymentMethodWithUsBankAccountCompliantMerchantTest(unittest.TestCase):
-    def setUp(self):
-        braintree.Configuration.configure(
-            braintree.Environment.Development,
-            "integration2_merchant_id",
-            "integration2_public_key",
-            "integration2_private_key"
-        )
-
-    def tearDown(self):
-        braintree.Configuration.configure(
-            braintree.Environment.Development,
-            "integration_merchant_id",
-            "integration_public_key",
-            "integration_private_key"
-        )
-
-    def test_create_with_nonce(self):
-        customer_id = Customer.create().customer.id
-        result = PaymentMethod.create({
-            "customer_id": customer_id,
-            "payment_method_nonce": TestHelper.generate_valid_us_bank_account_nonce(),
-            "options": {
-                "verification_merchant_account_id": TestHelper.another_us_bank_merchant_account_id
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        us_bank_account = result.payment_method
-        self.assertIsInstance(us_bank_account, UsBankAccount)
-        self.assertEqual(us_bank_account.routing_number, "021000021")
-        self.assertEqual(us_bank_account.last_4, "1234")
-        self.assertEqual(us_bank_account.account_type, "checking")
-        self.assertEqual(us_bank_account.account_holder_name, "Dan Schulman")
-        self.assertTrue(re.match(r".*CHASE.*", us_bank_account.bank_name))
-        self.assertEqual(us_bank_account.default, True)
-        self.assertEqual(us_bank_account.ach_mandate.text, "cl mandate text")
-        self.assertIsInstance(us_bank_account.ach_mandate.accepted_at, datetime)
-        self.assertEqual(us_bank_account.verified, False)
-
-        self.assertEqual(len(us_bank_account.verifications), 0)
-
-    def test_create_with_verification(self):
-        customer_id = Customer.create().customer.id
-        result = PaymentMethod.create({
-            "customer_id": customer_id,
-            "payment_method_nonce": TestHelper.generate_valid_us_bank_account_nonce("021000021", "1000000000"),
-            "options": {
-                "verification_merchant_account_id": TestHelper.another_us_bank_merchant_account_id,
-                "us_bank_account_verification_method": UsBankAccountVerification.VerificationMethod.NetworkCheck
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        us_bank_account = result.payment_method
-        self.assertIsInstance(us_bank_account, UsBankAccount)
-        self.assertEqual(us_bank_account.routing_number, "021000021")
-        self.assertEqual(us_bank_account.last_4, "0000")
-        self.assertEqual(us_bank_account.account_type, "checking")
-        self.assertEqual(us_bank_account.account_holder_name, "Dan Schulman")
-        self.assertTrue(re.match(r".*CHASE.*", us_bank_account.bank_name))
-        self.assertEqual(us_bank_account.default, True)
-        self.assertEqual(us_bank_account.ach_mandate.text, "cl mandate text")
-        self.assertIsInstance(us_bank_account.ach_mandate.accepted_at, datetime)
-        self.assertEqual(us_bank_account.verified, True)
-
-        self.assertEqual(len(us_bank_account.verifications), 1)
-
-        verification = us_bank_account.verifications[0]
-
-        self.assertEqual(verification.status, UsBankAccountVerification.Status.Verified)
-
-    def test_create_fails_with_invalid_us_bank_account_nonce(self):
-        customer_id = Customer.create().customer.id
-        result = PaymentMethod.create({
-            "customer_id": customer_id,
-            "payment_method_nonce": TestHelper.generate_invalid_us_bank_account_nonce(),
-            "options": {
-                "verification_merchant_account_id": TestHelper.another_us_bank_merchant_account_id,
-                "us_bank_account_verification_method": UsBankAccountVerification.VerificationMethod.NetworkCheck,
-            }
-        })
-
-        self.assertFalse(result.is_success)
-        error_code = result.errors.for_object("payment_method").on("payment_method_nonce")[0].code
-        self.assertEqual(ErrorCodes.PaymentMethod.PaymentMethodNonceUnknown, error_code)
-
-    def test_update_payment_method_with_verification(self):
-        customer_id = Customer.create().customer.id
-        result = PaymentMethod.create({
-            "customer_id": customer_id,
-            "payment_method_nonce": TestHelper.generate_valid_us_bank_account_nonce("021000021", "1000000000"),
-            "options": {
-                "verification_merchant_account_id": TestHelper.another_us_bank_merchant_account_id,
-                "us_bank_account_verification_method": UsBankAccountVerification.VerificationMethod.IndependentCheck
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        us_bank_account = result.payment_method
-
-        self.assertEqual(len(us_bank_account.verifications), 1)
-
-        verification = us_bank_account.verifications[0]
-
-        self.assertEqual(verification.status, UsBankAccountVerification.Status.Verified)
-        self.assertEqual(verification.verification_method, UsBankAccountVerification.VerificationMethod.IndependentCheck)
-
-        result = PaymentMethod.update(us_bank_account.token, {
-            "options": {
-                "verification_merchant_account_id": TestHelper.another_us_bank_merchant_account_id,
-                "us_bank_account_verification_method": UsBankAccountVerification.VerificationMethod.NetworkCheck,
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        us_bank_account = result.payment_method
-
-        self.assertEqual(len(us_bank_account.verifications), 2)
diff --git a/tests/integration/test_paypal_account.py b/tests/integration/test_paypal_account.py
deleted file mode 100644
index 00e647c..0000000
--- a/tests/integration/test_paypal_account.py
+++ /dev/null
@@ -1,161 +0,0 @@
-from tests.test_helper import *
-import time
-from braintree.test.nonces import Nonces
-
-class TestPayPalAccount(unittest.TestCase):
-    def test_find_returns_paypal_account(self):
-        customer_id = Customer.create().customer.id
-        result = PaymentMethod.create({
-            "customer_id": customer_id,
-            "payment_method_nonce": Nonces.PayPalFuturePayment
-        })
-        self.assertTrue(result.is_success)
-
-        found_account = PayPalAccount.find(result.payment_method.token)
-        self.assertNotEqual(None, found_account)
-        self.assertEqual(found_account.__class__, PayPalAccount)
-        self.assertEqual(found_account.token, result.payment_method.token)
-        self.assertNotEqual(None, found_account.image_url)
-        self.assertNotEqual(None, found_account.created_at)
-        self.assertNotEqual(None, found_account.updated_at)
-        self.assertIsNone(found_account.revoked_at)
-
-    def test_find_raises_on_not_found_token(self):
-        self.assertRaises(NotFoundError, PayPalAccount.find, "non-existant-token")
-
-    def test_find_will_not_return_credit_card(self):
-        credit_card = CreditCard.create({
-            "customer_id": Customer.create().customer.id,
-            "number": "4111111111111111",
-            "expiration_date": "12/2099"
-        }).credit_card
-
-        self.assertRaises(NotFoundError, PayPalAccount.find, credit_card.token)
-
-    def test_find_returns_subscriptions_associated_with_a_paypal_account(self):
-        customer_id = Customer.create().customer.id
-        payment_method_token = "paypal-account-" + str(int(time.time()))
-
-        nonce = TestHelper.nonce_for_paypal_account({
-            "consent_code": "consent-code",
-            "token": payment_method_token
-        })
-        result = PaymentMethod.create({
-            "payment_method_nonce": nonce,
-            "customer_id": customer_id
-        })
-        self.assertTrue(result.is_success)
-
-        token = result.payment_method.token
-
-        subscription1 = Subscription.create({
-            "payment_method_token": token,
-            "plan_id": TestHelper.trialless_plan["id"]
-        }).subscription
-
-        subscription2 = Subscription.create({
-            "payment_method_token": token,
-            "plan_id": TestHelper.trialless_plan["id"]
-        }).subscription
-
-        paypal_account = PayPalAccount.find(result.payment_method.token)
-        self.assertTrue(subscription1.id in [s.id for s in paypal_account.subscriptions])
-        self.assertTrue(subscription2.id in [s.id for s in paypal_account.subscriptions])
-
-    def test_find_retuns_billing_agreement_id_with_a_paypal_account(self):
-        customer_id = Customer.create().customer.id
-
-        result = PaymentMethod.create({
-            "payment_method_nonce": Nonces.PayPalBillingAgreement,
-            "customer_id": customer_id
-        })
-        self.assertTrue(result.is_success)
-
-        paypal_account = PayPalAccount.find(result.payment_method.token)
-        self.assertNotEqual(None, paypal_account.billing_agreement_id)
-
-    def test_delete_deletes_paypal_account(self):
-        result = PaymentMethod.create({
-            "customer_id": Customer.create().customer.id,
-            "payment_method_nonce": Nonces.PayPalFuturePayment
-        })
-        self.assertTrue(result.is_success)
-        paypal_account_token = result.payment_method.token
-
-        delete_result = PayPalAccount.delete(paypal_account_token)
-        self.assertTrue(delete_result.is_success)
-
-        self.assertRaises(NotFoundError, PayPalAccount.find, paypal_account_token)
-
-    def test_delete_raises_on_not_found(self):
-        self.assertRaises(NotFoundError, PayPalAccount.delete, "non-existant-token")
-
-    def test_delete_delete_wont_delete_credit_card(self):
-        credit_card = CreditCard.create({
-            "customer_id": Customer.create().customer.id,
-            "number": "4111111111111111",
-            "expiration_date": "12/2099"
-        }).credit_card
-
-        self.assertRaises(NotFoundError, PayPalAccount.delete, credit_card.token)
-
-    def test_update_can_update_token_and_default(self):
-        customer_id = Customer.create().customer.id
-
-        CreditCard.create({
-            "customer_id": customer_id,
-            "number": "4111111111111111",
-            "expiration_date": "12/2099"
-        })
-
-        result = PaymentMethod.create({
-            "customer_id": customer_id,
-            "payment_method_nonce": Nonces.PayPalFuturePayment
-        })
-        self.assertTrue(result.is_success)
-
-        old_token = result.payment_method.token
-        new_token = "new-token-%s" % int(round(time.time() * 1000))
-        result = PayPalAccount.update(old_token, {
-            "token": new_token,
-            "options": {"make_default": True}
-        })
-
-        self.assertTrue(result.is_success)
-        updated_account = PayPalAccount.find(new_token)
-        self.assertEqual(updated_account.default, True)
-
-    def test_update_returns_validation_errors(self):
-        payment_method_token = "payment-token-%s" % int(round(time.time() * 1000))
-        customer_id = Customer.create().customer.id
-        CreditCard.create({
-            "token": payment_method_token,
-            "customer_id": customer_id,
-            "number": "4111111111111111",
-            "expiration_date": "12/2099"
-        })
-
-        result = PaymentMethod.create({
-            "customer_id": customer_id,
-            "payment_method_nonce": Nonces.PayPalFuturePayment
-        })
-        self.assertTrue(result.is_success)
-
-        old_token = result.payment_method.token
-        result = PayPalAccount.update(old_token, {
-            "token": payment_method_token,
-        })
-        self.assertFalse(result.is_success)
-
-        token_errors = result.errors.for_object("paypal_account").on("token")
-        self.assertEqual(1, len(token_errors))
-        self.assertEqual(ErrorCodes.PayPalAccount.TokenIsInUse, token_errors[0].code)
-
-        result = PayPalAccount.update(old_token, {
-            "token": payment_method_token,
-        })
-        self.assertFalse(result.is_success)
-
-        token_errors = result.errors.for_object("paypal_account").on("token")
-        self.assertEqual(1, len(token_errors))
-        self.assertEqual(ErrorCodes.PayPalAccount.TokenIsInUse, token_errors[0].code)
diff --git a/tests/integration/test_plan.py b/tests/integration/test_plan.py
deleted file mode 100644
index 04c0fc1..0000000
--- a/tests/integration/test_plan.py
+++ /dev/null
@@ -1,79 +0,0 @@
-from tests.test_helper import *
-
-class TestPlan(unittest.TestCase):
-
-    def test_all_returns_empty_list(self):
-        Configuration.configure(
-            Environment.Development,
-            "test_merchant_id",
-            "test_public_key",
-            "test_private_key"
-        )
-        plans = Plan.all()
-        self.assertEqual([], plans)
-        Configuration.configure(
-            Environment.Development,
-            "integration_merchant_id",
-            "integration_public_key",
-            "integration_private_key"
-        )
-
-    def test_all_returns_all_the_plans(self):
-        plan_token = str(random.randint(1, 1000000))
-        attributes = {
-            "id": plan_token,
-            "billing_day_of_month": 1,
-            "billing_frequency": 1,
-            "currency_iso_code": "USD",
-            "description": "some description",
-            "name": "python test plan",
-            "number_of_billing_cycles": 1,
-            "price": "1.00",
-        }
-
-        Configuration.instantiate().http().post(Configuration.instantiate().base_merchant_path() + "/plans/create_plan_for_tests", {"plan": attributes})
-
-        add_on_attributes = {
-            "amount": "100.00",
-            "description": "some description",
-            "plan_id": plan_token,
-            "kind": "add_on",
-            "name": "python_add_on",
-            "never_expires": False,
-            "number_of_billing_cycles": 1
-        }
-
-        Configuration.instantiate().http().post(Configuration.instantiate().base_merchant_path() + "/modifications/create_modification_for_tests", {"modification": add_on_attributes})
-        discount_attributes = {
-            "amount": "100.00",
-            "description": "some description",
-            "plan_id": plan_token,
-            "kind": "discount",
-            "name": "python_discount",
-            "never_expires": False,
-            "number_of_billing_cycles": 1
-        }
-
-        Configuration.instantiate().http().post(Configuration.instantiate().base_merchant_path() + "/modifications/create_modification_for_tests", {"modification": discount_attributes})
-
-        plans = Plan.all()
-
-        for plan in plans:
-            if plan.id == plan_token:
-                actual_plan = plan
-
-        self.assertNotEqual(None, actual_plan)
-
-        self.assertEqual(1, attributes["billing_day_of_month"])
-        self.assertEqual(1, attributes["billing_frequency"])
-        self.assertEqual("USD", attributes["currency_iso_code"])
-        self.assertEqual("some description", attributes["description"])
-        self.assertEqual("python test plan", attributes["name"])
-        self.assertEqual(1, attributes["number_of_billing_cycles"])
-        self.assertEqual("1.00", attributes["price"])
-
-        self.assertEqual(1, len(actual_plan.add_ons))
-        self.assertEqual(add_on_attributes["name"], actual_plan.add_ons[0].name)
-
-        self.assertEqual(1, len(actual_plan.discounts))
-        self.assertEqual(discount_attributes["name"], actual_plan.discounts[0].name)
diff --git a/tests/integration/test_samsung_pay.py b/tests/integration/test_samsung_pay.py
deleted file mode 100644
index 53302f8..0000000
--- a/tests/integration/test_samsung_pay.py
+++ /dev/null
@@ -1,132 +0,0 @@
-from tests.test_helper import *
-
-class TestSamsungPay(unittest.TestCase):
-    def test_create_from_nonce(self):
-        customer = Customer.create().customer
-        result = PaymentMethod.create({
-            "customer_id": customer.id,
-            "payment_method_nonce": Nonces.SamsungPayVisa
-        })
-
-        self.assertTrue(result.is_success)
-        samsung_pay_card = result.payment_method
-        self.assertIsInstance(samsung_pay_card, braintree.SamsungPayCard)
-        self.assertIsNotNone(samsung_pay_card.bin)
-        self.assertIsNotNone(samsung_pay_card.card_type)
-        self.assertIsNotNone(samsung_pay_card.commercial)
-        self.assertIsNotNone(samsung_pay_card.country_of_issuance)
-        self.assertIsNotNone(samsung_pay_card.created_at)
-        self.assertIsNotNone(samsung_pay_card.customer_id)
-        self.assertIsNotNone(samsung_pay_card.customer_location)
-        self.assertIsNotNone(samsung_pay_card.debit)
-        self.assertIsNotNone(samsung_pay_card.default)
-        self.assertIsNotNone(samsung_pay_card.durbin_regulated)
-        self.assertIsNotNone(samsung_pay_card.expiration_date)
-        self.assertIsNotNone(samsung_pay_card.expiration_month)
-        self.assertIsNotNone(samsung_pay_card.expiration_year)
-        self.assertIsNotNone(samsung_pay_card.expired)
-        self.assertIsNotNone(samsung_pay_card.healthcare)
-        self.assertIsNotNone(samsung_pay_card.image_url)
-        self.assertIsNotNone(samsung_pay_card.issuing_bank)
-        self.assertIsNotNone(samsung_pay_card.last_4)
-        self.assertIsNotNone(samsung_pay_card.masked_number)
-        self.assertIsNotNone(samsung_pay_card.payroll)
-        self.assertIsNotNone(samsung_pay_card.prepaid)
-        self.assertIsNotNone(samsung_pay_card.product_id)
-        self.assertIsNotNone(samsung_pay_card.subscriptions)
-        self.assertIsNotNone(samsung_pay_card.token)
-        self.assertIsNotNone(samsung_pay_card.unique_number_identifier)
-        self.assertIsNotNone(samsung_pay_card.updated_at)
-
-    def test_create_from_nonce_customer_attr(self):
-        customer = Customer.create().customer
-        result = PaymentMethod.create({
-            "customer_id": customer.id,
-            "payment_method_nonce": Nonces.SamsungPayVisa
-        })
-
-        samsung_pay_cards = Customer.find(customer.id).samsung_pay_cards
-        self.assertEqual(len(samsung_pay_cards), 1)
-        self.assertEqual(result.payment_method.token, samsung_pay_cards[0].token)
-
-    def test_create_from_nonce_with_name_and_address(self):
-        customer = Customer.create().customer
-        result = PaymentMethod.create({
-            "customer_id": customer.id,
-            "payment_method_nonce": Nonces.SamsungPayVisa,
-            "cardholder_name": "Gronk",
-            "billing_address": {
-                "street_address": "123 Abc Way",
-                "locality": "Chicago",
-                "region": "Illinois",
-                "postal_code": "60622",
-                "country_code_alpha2": "MX",
-                "country_code_alpha3": "MEX",
-                "country_code_numeric": "484",
-                "country_name": "Mexico"
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertEqual("Gronk", result.payment_method.cardholder_name)
-
-        address = result.payment_method.billing_address
-        self.assertEqual("123 Abc Way", address.street_address)
-        self.assertEqual("Chicago", address.locality)
-        self.assertEqual("Illinois", address.region)
-        self.assertEqual("60622", address.postal_code)
-        self.assertEqual("MX", address.country_code_alpha2)
-        self.assertEqual("MEX", address.country_code_alpha3)
-        self.assertEqual("484", address.country_code_numeric)
-        self.assertEqual("Mexico", address.country_name)
-
-    def test_search_for_transaction(self):
-        result = Transaction.sale({
-            "payment_method_nonce": Nonces.SamsungPayVisa,
-            "amount": "1.69"
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.payment_instrument_type == PaymentInstrumentType.SamsungPayCard
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(transaction.id, collection.first.id)
-
-    def test_create_transaction_from_nonce_and_vault(self):
-        customer = Customer.create().customer
-        result = Transaction.sale({
-            "payment_method_nonce": Nonces.SamsungPayVisa,
-            "customer_id": customer.id,
-            "amount": "69.69",
-            "options": {
-                "store_in_vault": True
-            }
-        })
-
-        self.assertTrue(result.is_success)
-
-        samsung_pay_card_details = result.transaction.samsung_pay_card_details
-        self.assertIsInstance(samsung_pay_card_details, braintree.SamsungPayCard)
-        self.assertIsNotNone(samsung_pay_card_details.bin)
-        self.assertIsNotNone(samsung_pay_card_details.card_type)
-        self.assertIsNotNone(samsung_pay_card_details.commercial)
-        self.assertIsNotNone(samsung_pay_card_details.country_of_issuance)
-        self.assertIsNotNone(samsung_pay_card_details.debit)
-        self.assertIsNotNone(samsung_pay_card_details.durbin_regulated)
-        self.assertIsNotNone(samsung_pay_card_details.expiration_date)
-        self.assertIsNotNone(samsung_pay_card_details.expiration_year)
-        self.assertIsNotNone(samsung_pay_card_details.expiration_month)
-        self.assertIsNotNone(samsung_pay_card_details.healthcare)
-        self.assertIsNotNone(samsung_pay_card_details.image_url)
-        self.assertIsNotNone(samsung_pay_card_details.issuing_bank)
-        self.assertIsNotNone(samsung_pay_card_details.last_4)
-        self.assertIsNotNone(samsung_pay_card_details.payroll)
-        self.assertIsNotNone(samsung_pay_card_details.prepaid)
-        self.assertIsNotNone(samsung_pay_card_details.product_id)
-        self.assertIsNotNone(samsung_pay_card_details.token)
-
diff --git a/tests/integration/test_search.py b/tests/integration/test_search.py
deleted file mode 100644
index 9e3691d..0000000
--- a/tests/integration/test_search.py
+++ /dev/null
@@ -1,369 +0,0 @@
-from tests.test_helper import *
-
-class TestSearch(unittest.TestCase):
-    def test_text_node_is(self):
-        credit_card = Customer.create({
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2010",
-            }
-        }).customer.credit_cards[0]
-
-        trial_subscription = Subscription.create({
-            "payment_method_token": credit_card.token,
-            "plan_id": TestHelper.trial_plan["id"]
-        }).subscription
-
-        trialless_subscription = Subscription.create({
-            "payment_method_token": credit_card.token,
-            "plan_id": TestHelper.trialless_plan["id"]
-        }).subscription
-
-        collection = Subscription.search([
-            SubscriptionSearch.plan_id == "integration_trial_plan"
-        ])
-
-        self.assertTrue(TestHelper.includes(collection, trial_subscription))
-        self.assertFalse(TestHelper.includes(collection, trialless_subscription))
-
-    def test_text_node_is_not(self):
-        credit_card = Customer.create({
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2010",
-            }
-        }).customer.credit_cards[0]
-
-        trial_subscription = Subscription.create({
-            "payment_method_token": credit_card.token,
-            "plan_id": TestHelper.trial_plan["id"]
-        }).subscription
-
-        trialless_subscription = Subscription.create({
-            "payment_method_token": credit_card.token,
-            "plan_id": TestHelper.trialless_plan["id"]
-        }).subscription
-
-        collection = Subscription.search([
-            SubscriptionSearch.plan_id != "integration_trialless_plan"
-        ])
-
-        self.assertTrue(TestHelper.includes(collection, trial_subscription))
-        self.assertFalse(TestHelper.includes(collection, trialless_subscription))
-
-    def test_text_node_starts_with(self):
-        credit_card = Customer.create({
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2010",
-            }
-        }).customer.credit_cards[0]
-
-        trial_subscription = Subscription.create({
-            "payment_method_token": credit_card.token,
-            "plan_id": TestHelper.trial_plan["id"]
-        }).subscription
-
-        trialless_subscription = Subscription.create({
-            "payment_method_token": credit_card.token,
-            "plan_id": TestHelper.trialless_plan["id"]
-        }).subscription
-
-        collection = Subscription.search([
-            SubscriptionSearch.plan_id.starts_with("integration_trial_p")
-        ])
-
-        self.assertTrue(TestHelper.includes(collection, trial_subscription))
-        self.assertFalse(TestHelper.includes(collection, trialless_subscription))
-
-    def test_text_node_ends_with(self):
-        credit_card = Customer.create({
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2010",
-            }
-
-        }).customer.credit_cards[0]
-        trial_subscription = Subscription.create({
-            "payment_method_token": credit_card.token,
-            "plan_id": TestHelper.trial_plan["id"]
-        }).subscription
-
-        trialless_subscription = Subscription.create({
-            "payment_method_token": credit_card.token,
-            "plan_id": TestHelper.trialless_plan["id"]
-        }).subscription
-
-        collection = Subscription.search([
-            SubscriptionSearch.plan_id.ends_with("trial_plan")
-        ])
-
-        self.assertTrue(TestHelper.includes(collection, trial_subscription))
-        self.assertFalse(TestHelper.includes(collection, trialless_subscription))
-
-    def test_text_node_contains(self):
-        credit_card = Customer.create({
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2010",
-            }
-        }).customer.credit_cards[0]
-
-        trial_subscription = Subscription.create({
-            "payment_method_token": credit_card.token,
-            "plan_id": TestHelper.trial_plan["id"]
-        }).subscription
-
-        trialless_subscription = Subscription.create({
-            "payment_method_token": credit_card.token,
-            "plan_id": TestHelper.trialless_plan["id"]
-        }).subscription
-
-        collection = Subscription.search([
-            SubscriptionSearch.plan_id.contains("rial_pl")
-        ])
-
-        self.assertTrue(TestHelper.includes(collection, trial_subscription))
-        self.assertFalse(TestHelper.includes(collection, trialless_subscription))
-
-    def test_multiple_value_node_in_list(self):
-        credit_card = Customer.create({
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2010",
-            }
-        }).customer.credit_cards[0]
-
-        active_subscription = Subscription.create({
-            "payment_method_token": credit_card.token,
-            "plan_id": TestHelper.trialless_plan["id"]
-        }).subscription
-
-        canceled_subscription = Subscription.create({
-            "payment_method_token": credit_card.token,
-            "plan_id": TestHelper.trialless_plan["id"]
-        }).subscription
-        Subscription.cancel(canceled_subscription.id)
-
-        collection = Subscription.search([
-            SubscriptionSearch.status.in_list([Subscription.Status.Active, Subscription.Status.Canceled])
-        ])
-
-        self.assertTrue(TestHelper.includes(collection, active_subscription))
-        self.assertTrue(TestHelper.includes(collection, canceled_subscription))
-
-    def test_multiple_value_node_in_list_as_arg_list(self):
-        credit_card = Customer.create({
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2010",
-            }
-        }).customer.credit_cards[0]
-
-        active_subscription = Subscription.create({
-            "payment_method_token": credit_card.token,
-            "plan_id": TestHelper.trialless_plan["id"]
-        }).subscription
-
-        canceled_subscription = Subscription.create({
-            "payment_method_token": credit_card.token,
-            "plan_id": TestHelper.trialless_plan["id"]
-        }).subscription
-        Subscription.cancel(canceled_subscription.id)
-
-        collection = Subscription.search([
-            SubscriptionSearch.status.in_list(Subscription.Status.Active, Subscription.Status.Canceled)
-        ])
-
-        self.assertTrue(TestHelper.includes(collection, active_subscription))
-        self.assertTrue(TestHelper.includes(collection, canceled_subscription))
-
-    def test_multiple_value_node_is(self):
-        credit_card = Customer.create({
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2010",
-            }
-        }).customer.credit_cards[0]
-
-        active_subscription = Subscription.create({
-            "payment_method_token": credit_card.token,
-            "plan_id": TestHelper.trialless_plan["id"]
-        }).subscription
-
-        canceled_subscription = Subscription.create({
-            "payment_method_token": credit_card.token,
-            "plan_id": TestHelper.trialless_plan["id"]
-        }).subscription
-        Subscription.cancel(canceled_subscription.id)
-
-        collection = Subscription.search([
-            SubscriptionSearch.status == Subscription.Status.Active
-        ])
-
-        self.assertTrue(TestHelper.includes(collection, active_subscription))
-        self.assertFalse(TestHelper.includes(collection, canceled_subscription))
-
-    def test_range_node_min(self):
-        name = "Henrietta Livingston%s" % random.randint(1, 100000)
-        Transaction.sale({
-            "amount": "1500.00",
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2012",
-                "cardholder_name": name
-            }
-        })
-
-        t_1800 = Transaction.sale({
-            "amount": "1800.00",
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2012",
-                "cardholder_name": name
-            }
-        }).transaction
-
-        collection = Transaction.search([
-            TransactionSearch.credit_card_cardholder_name == name,
-            TransactionSearch.amount >= "1700"
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(t_1800.id, collection.first.id)
-
-        collection = Transaction.search([
-            TransactionSearch.credit_card_cardholder_name == name,
-            TransactionSearch.amount.greater_than_or_equal_to("1700")
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(t_1800.id, collection.first.id)
-
-    def test_range_node_max(self):
-        name = "Henrietta Livingston%s" % random.randint(1, 100000)
-        t_1500 = Transaction.sale({
-            "amount": "1500.00",
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2012",
-                "cardholder_name": name
-            }
-        }).transaction
-
-        Transaction.sale({
-            "amount": "1800.00",
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2012",
-                "cardholder_name": name
-            }
-        })
-
-        collection = Transaction.search([
-            TransactionSearch.credit_card_cardholder_name == name,
-            TransactionSearch.amount <= "1700"
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(t_1500.id, collection.first.id)
-
-        collection = Transaction.search([
-            TransactionSearch.credit_card_cardholder_name == name,
-            TransactionSearch.amount.less_than_or_equal_to("1700")
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(t_1500.id, collection.first.id)
-
-    def test_range_node_is(self):
-        name = "Henrietta Livingston%s" % random.randint(1, 100000)
-        Transaction.sale({
-            "amount": "1500.00",
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2012",
-                "cardholder_name": name
-            }
-        })
-
-        t_1800 = Transaction.sale({
-            "amount": "1800.00",
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2012",
-                "cardholder_name": name
-            }
-        }).transaction
-
-        collection = Transaction.search([
-            TransactionSearch.credit_card_cardholder_name == name,
-            TransactionSearch.amount == "1800"
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(t_1800.id, collection.first.id)
-
-    def test_range_node_between(self):
-        name = "Henrietta Livingston%s" % random.randint(1, 100000)
-        Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2012",
-                "cardholder_name": name
-            }
-        })
-
-        t_1500 = Transaction.sale({
-            "amount": "1500.00",
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2012",
-                "cardholder_name": name
-            }
-        }).transaction
-
-        Transaction.sale({
-            "amount": "1800.00",
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2012",
-                "cardholder_name": name
-            }
-        })
-
-        collection = Transaction.search([
-            TransactionSearch.credit_card_cardholder_name == name,
-            TransactionSearch.amount.between("1100", "1600")
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(t_1500.id, collection.first.id)
-
-    def test_search_on_multiple_values(self):
-        credit_card = Customer.create({
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2010",
-            }
-        }).customer.credit_cards[0]
-
-        active_subscription = Subscription.create({
-            "payment_method_token": credit_card.token,
-            "plan_id": TestHelper.trialless_plan["id"]
-        }).subscription
-
-        canceled_subscription = Subscription.create({
-            "payment_method_token": credit_card.token,
-            "plan_id": TestHelper.trialless_plan["id"]
-        }).subscription
-        Subscription.cancel(canceled_subscription.id)
-
-        collection = Subscription.search([
-            SubscriptionSearch.plan_id == "integration_trialless_plan",
-            SubscriptionSearch.status.in_list([Subscription.Status.Active])
-        ])
-
-        self.assertTrue(TestHelper.includes(collection, active_subscription))
-        self.assertFalse(TestHelper.includes(collection, canceled_subscription))
diff --git a/tests/integration/test_settlement_batch_summary.py b/tests/integration/test_settlement_batch_summary.py
deleted file mode 100644
index 0dbfa04..0000000
--- a/tests/integration/test_settlement_batch_summary.py
+++ /dev/null
@@ -1,62 +0,0 @@
-from tests.test_helper import *
-
-class TestSettlementBatchSummary(unittest.TestCase):
-    possible_gateway_time_zone_offsets = (5, 4)
-
-    def test_generate_returns_empty_collection_if_there_is_no_data(self):
-        result = SettlementBatchSummary.generate('2011-01-01')
-
-        self.assertTrue(result.is_success)
-        self.assertEqual([], result.settlement_batch_summary.records)
-
-    def test_generate_returns_error_if_date_can_not_be_parsed(self):
-        result = SettlementBatchSummary.generate('THIS AINT NO DATE')
-        self.assertFalse(result.is_success)
-
-        settlement_date_errors = result.errors.for_object('settlement_batch_summary').on('settlement_date')
-        self.assertEqual(1, len(settlement_date_errors))
-        self.assertEqual(ErrorCodes.SettlementBatchSummary.SettlementDateIsInvalid, settlement_date_errors[0].code)
-
-    def test_generate_returns_transactions_settled_on_a_given_day(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2012",
-                "cardholder_name": "Sergio Ramos"
-            },
-            "merchant_account_id": "sandbox_credit_card",
-            "options": {"submit_for_settlement": True}
-        })
-
-        result = TestHelper.settle_transaction(result.transaction.id)
-        settlement_date = result.transaction.settlement_batch_id.split('_')[0]
-
-        result = SettlementBatchSummary.generate(settlement_date)
-        self.assertTrue(result.is_success)
-        visa_records = [row for row in result.settlement_batch_summary.records
-                if row['card_type'] == 'Visa'
-                and row['merchant_account_id'] == 'sandbox_credit_card'][0]
-        count = int(visa_records['count'])
-        self.assertGreaterEqual(count, 1)
-
-    def test_generate_can_be_grouped_by_a_custom_field(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2012",
-                "cardholder_name": "Sergio Ramos"
-            },
-            "options": {"submit_for_settlement": True},
-            "custom_fields": {
-                "store_me": 1
-            }
-        })
-
-        result = TestHelper.settle_transaction(result.transaction.id)
-        settlement_date = result.transaction.settlement_batch_id.split('_')[0]
-
-        result = SettlementBatchSummary.generate(settlement_date, 'store_me')
-        self.assertTrue(result.is_success)
-        self.assertTrue('store_me' in result.settlement_batch_summary.records[0])
diff --git a/tests/integration/test_subscription.py b/tests/integration/test_subscription.py
deleted file mode 100644
index 0277df4..0000000
--- a/tests/integration/test_subscription.py
+++ /dev/null
@@ -1,1458 +0,0 @@
-from tests.test_helper import *
-from braintree.test.nonces import Nonces
-from datetime import date, timedelta
-
-class TestSubscription(unittest.TestCase):
-    def setUp(self):
-        self.credit_card = Customer.create({
-            "first_name": "Mike",
-            "last_name": "Jones",
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2010",
-                "cvv": "100"
-            }
-        }).customer.credit_cards[0]
-
-        self.updateable_subscription = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "price": Decimal("54.32"),
-            "plan_id": TestHelper.trialless_plan["id"]
-        }).subscription
-
-
-    def test_create_returns_successful_result_if_valid(self):
-        result = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.trialless_plan["id"]
-        })
-
-        self.assertTrue(result.is_success)
-        subscription = result.subscription
-        self.assertNotEqual(None, re.search(r"\A\w{6}\Z", subscription.id))
-        self.assertEqual(Decimal("12.34"), subscription.price)
-        self.assertEqual(Decimal("12.34"), subscription.next_bill_amount)
-        self.assertEqual(Decimal("12.34"), subscription.next_billing_period_amount)
-        self.assertEqual(Subscription.Status.Active, subscription.status)
-        self.assertEqual("integration_trialless_plan", subscription.plan_id)
-        self.assertEqual(TestHelper.default_merchant_account_id, subscription.merchant_account_id)
-        self.assertEqual(Decimal("0.00"), subscription.balance)
-
-        self.assertEqual(date, type(subscription.first_billing_date))
-        self.assertEqual(date, type(subscription.next_billing_date))
-        self.assertEqual(date, type(subscription.billing_period_start_date))
-        self.assertEqual(date, type(subscription.billing_period_end_date))
-        self.assertEqual(date, type(subscription.paid_through_date))
-
-        self.assertEqual(datetime, type(subscription.created_at))
-        self.assertEqual(datetime, type(subscription.updated_at))
-
-        self.assertEqual(1, subscription.current_billing_cycle)
-        self.assertEqual(0, subscription.failure_count)
-        self.assertEqual(self.credit_card.token, subscription.payment_method_token)
-
-        self.assertEqual(Subscription.Status.Active, subscription.status_history[0].status)
-        self.assertEqual(Decimal("12.34"), subscription.status_history[0].price)
-        self.assertEqual(Decimal("0.00"), subscription.status_history[0].balance)
-        self.assertEqual(Subscription.Source.Api, subscription.status_history[0].subscription_source)
-        self.assertEqual("USD", subscription.status_history[0].currency_iso_code)
-        self.assertEqual(TestHelper.trialless_plan["id"], subscription.status_history[0].plan_id)
-
-    def test_create_returns_successful_result_with_payment_method_nonce(self):
-        config = Configuration.instantiate()
-        customer_id = Customer.create().customer.id
-        parsed_client_token = TestHelper.generate_decoded_client_token({"customer_id": customer_id})
-        authorization_fingerprint = json.loads(parsed_client_token)["authorizationFingerprint"]
-        http = ClientApiHttp(config, {
-            "authorization_fingerprint": authorization_fingerprint,
-            "shared_customer_identifier": "fake_identifier",
-            "shared_customer_identifier_type": "testing"
-        })
-        _, response = http.add_card({
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_month": "11",
-                "expiration_year": "2099",
-            },
-            "share": True
-        })
-        nonce = json.loads(response)["creditCards"][0]["nonce"]
-
-        result = Subscription.create({
-            "payment_method_nonce": nonce,
-            "plan_id": TestHelper.trialless_plan["id"]
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.subscription.transactions[0]
-        self.assertEqual("411111", transaction.credit_card_details.bin)
-
-
-    def test_create_can_set_the_id(self):
-        new_id = str(random.randint(1, 1000000))
-        result = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.trialless_plan["id"],
-            "id": new_id
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertEqual(new_id, result.subscription.id)
-
-    def test_create_can_set_the_merchant_account_id(self):
-        result = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.trialless_plan["id"],
-            "merchant_account_id": TestHelper.non_default_merchant_account_id
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertEqual(TestHelper.non_default_merchant_account_id, result.subscription.merchant_account_id)
-
-    def test_create_defaults_to_plan_without_trial(self):
-        subscription = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.trialless_plan["id"],
-        }).subscription
-
-        self.assertEqual(TestHelper.trialless_plan["trial_period"], subscription.trial_period)
-        self.assertEqual(None, subscription.trial_duration)
-        self.assertEqual(None, subscription.trial_duration_unit)
-
-    def test_create_defaults_to_plan_with_trial(self):
-        subscription = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.trial_plan["id"],
-        }).subscription
-
-        self.assertEqual(TestHelper.trial_plan["trial_period"], subscription.trial_period)
-        self.assertEqual(TestHelper.trial_plan["trial_duration"], subscription.trial_duration)
-        self.assertEqual(TestHelper.trial_plan["trial_duration_unit"], subscription.trial_duration_unit)
-
-    def test_create_and_override_plan_with_trial(self):
-        subscription = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.trial_plan["id"],
-            "trial_duration": 5,
-            "trial_duration_unit": Subscription.TrialDurationUnit.Month
-        }).subscription
-
-        self.assertEqual(True, subscription.trial_period)
-        self.assertEqual(5, subscription.trial_duration)
-        self.assertEqual(Subscription.TrialDurationUnit.Month, subscription.trial_duration_unit)
-
-    def test_create_and_override_trial_period(self):
-        subscription = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.trial_plan["id"],
-            "trial_period": False
-        }).subscription
-
-        self.assertEqual(False, subscription.trial_period)
-
-    def test_create_and_override_number_of_billing_cycles(self):
-        subscription = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.trial_plan["id"],
-            "number_of_billing_cycles": 10
-        }).subscription
-
-        self.assertEqual(10, subscription.number_of_billing_cycles)
-
-    def test_create_and_override_number_of_billing_cycles_to_never_expire(self):
-        subscription = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.trial_plan["id"],
-            "never_expires": True
-        }).subscription
-
-        self.assertEqual(None, subscription.number_of_billing_cycles)
-
-    def test_create_creates_a_transaction_if_no_trial_period(self):
-        subscription = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.trialless_plan["id"],
-        }).subscription
-
-        self.assertEqual(1, len(subscription.transactions))
-        transaction = subscription.transactions[0]
-        self.assertEqual(Transaction, type(transaction))
-        self.assertEqual(TestHelper.trialless_plan["price"], transaction.amount)
-        self.assertEqual("sale", transaction.type)
-        self.assertEqual(subscription.id, transaction.subscription_id)
-
-    def test_create_has_transaction_with_billing_period_dates(self):
-        subscription = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.trialless_plan["id"],
-        }).subscription
-        transaction = subscription.transactions[0]
-        self.assertEqual(subscription.billing_period_start_date, transaction.subscription_details.billing_period_start_date)
-        self.assertEqual(subscription.billing_period_end_date, transaction.subscription_details.billing_period_end_date)
-
-    def test_create_returns_a_transaction_if_transaction_is_declined(self):
-        result = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.trialless_plan["id"],
-            "price": TransactionAmounts.Decline
-        })
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(Transaction.Status.ProcessorDeclined, result.transaction.status)
-
-    def test_create_doesnt_creates_a_transaction_if_trial_period(self):
-        subscription = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.trial_plan["id"],
-        }).subscription
-
-        self.assertEqual(0, len(subscription.transactions))
-
-    def test_create_with_error_result(self):
-        result = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.trial_plan["id"],
-            "id": "invalid token"
-        })
-        self.assertFalse(result.is_success)
-
-        id_errors = result.errors.for_object("subscription").on("id")
-        self.assertEqual(1, len(id_errors))
-        self.assertEqual("81906", id_errors[0].code)
-
-    def test_create_inherits_billing_day_of_month_from_plan(self):
-        result = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.billing_day_of_month_plan["id"],
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertEqual(5, result.subscription.billing_day_of_month)
-
-    def test_create_allows_overriding_billing_day_of_month(self):
-        result = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.billing_day_of_month_plan["id"],
-            "billing_day_of_month": 19
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertEqual(19, result.subscription.billing_day_of_month)
-
-    def test_create_allows_overriding_billing_day_of_month_with_start_immediately(self):
-        result = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.billing_day_of_month_plan["id"],
-            "options": {
-                "start_immediately": True
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertEqual(1, len(result.subscription.transactions))
-
-    def test_create_allows_specifying_first_billing_date(self):
-        result = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.billing_day_of_month_plan["id"],
-            "first_billing_date": date.today() + timedelta(days=3)
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertEqual(date.today() + timedelta(days=3), result.subscription.first_billing_date)
-        self.assertEqual(Subscription.Status.Pending, result.subscription.status)
-
-    def test_create_does_not_allow_first_billing_date_in_the_past(self):
-        result = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.billing_day_of_month_plan["id"],
-            "first_billing_date": date.today() - timedelta(days=3)
-        })
-        self.assertFalse(result.is_success)
-
-        billing_date_errors = result.errors.for_object("subscription").on("first_billing_date")
-        self.assertEqual(1, len(billing_date_errors))
-        self.assertEqual(ErrorCodes.Subscription.FirstBillingDateCannotBeInThePast, billing_date_errors[0].code)
-
-    def test_create_does_not_inherit_add_ons_or_discounts_from_the_plan_when_flag_is_set(self):
-        subscription = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.add_on_discount_plan["id"],
-            "options": {
-                "do_not_inherit_add_ons_or_discounts": True
-            }
-        }).subscription
-
-        self.assertEqual(0, len(subscription.add_ons))
-        self.assertEqual(0, len(subscription.discounts))
-
-    def test_create_inherits_add_ons_and_discounts_from_the_plan_when_not_specified(self):
-        subscription = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.add_on_discount_plan["id"]
-        }).subscription
-
-        self.assertEqual(2, len(subscription.add_ons))
-        add_ons = sorted(subscription.add_ons, key=lambda add_on: add_on.id)
-
-        self.assertEqual("increase_10", add_ons[0].id)
-        self.assertEqual(Decimal("10.00"), add_ons[0].amount)
-        self.assertEqual(1, add_ons[0].quantity)
-        self.assertEqual(None, add_ons[0].number_of_billing_cycles)
-        self.assertTrue(add_ons[0].never_expires)
-        self.assertEqual(0, add_ons[0].current_billing_cycle)
-
-        self.assertEqual("increase_20", add_ons[1].id)
-        self.assertEqual(Decimal("20.00"), add_ons[1].amount)
-        self.assertEqual(1, add_ons[1].quantity)
-        self.assertEqual(None, add_ons[1].number_of_billing_cycles)
-        self.assertTrue(add_ons[1].never_expires)
-        self.assertEqual(0, add_ons[1].current_billing_cycle)
-
-        self.assertEqual(2, len(subscription.discounts))
-        discounts = sorted(subscription.discounts, key=lambda discount: discount.id)
-
-        self.assertEqual("discount_11", discounts[0].id)
-        self.assertEqual(Decimal("11.00"), discounts[0].amount)
-        self.assertEqual(1, discounts[0].quantity)
-        self.assertEqual(None, discounts[0].number_of_billing_cycles)
-        self.assertTrue(discounts[0].never_expires)
-        self.assertEqual(0, discounts[0].current_billing_cycle)
-
-        self.assertEqual("discount_7", discounts[1].id)
-        self.assertEqual(Decimal("7.00"), discounts[1].amount)
-        self.assertEqual(1, discounts[1].quantity)
-        self.assertEqual(None, discounts[1].number_of_billing_cycles)
-        self.assertTrue(discounts[1].never_expires)
-        self.assertEqual(0, discounts[1].current_billing_cycle)
-
-    def test_create_allows_overriding_of_inherited_add_ons_and_discounts(self):
-        subscription = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.add_on_discount_plan["id"],
-            "add_ons": {
-                "update": [
-                    {
-                        "amount": Decimal("50.00"),
-                        "existing_id": "increase_10",
-                        "quantity": 2,
-                        "number_of_billing_cycles": 5
-                    },
-                    {
-                        "amount": Decimal("100.00"),
-                        "existing_id": "increase_20",
-                        "quantity": 4,
-                        "never_expires": True
-                    }
-                ]
-            },
-            "discounts": {
-                "update": [
-                    {
-                        "amount": Decimal("15.00"),
-                        "existing_id": "discount_7",
-                        "quantity": 3,
-                        "number_of_billing_cycles": 19
-                    }
-                ]
-            }
-        }).subscription
-
-        self.assertEqual(2, len(subscription.add_ons))
-        add_ons = sorted(subscription.add_ons, key=lambda add_on: add_on.id)
-
-        self.assertEqual("increase_10", add_ons[0].id)
-        self.assertEqual(Decimal("50.00"), add_ons[0].amount)
-        self.assertEqual(2, add_ons[0].quantity)
-        self.assertEqual(5, add_ons[0].number_of_billing_cycles)
-        self.assertFalse(add_ons[0].never_expires)
-        self.assertEqual(0, add_ons[0].current_billing_cycle)
-
-        self.assertEqual("increase_20", add_ons[1].id)
-        self.assertEqual(Decimal("100.00"), add_ons[1].amount)
-        self.assertEqual(4, add_ons[1].quantity)
-        self.assertEqual(None, add_ons[1].number_of_billing_cycles)
-        self.assertTrue(add_ons[1].never_expires)
-        self.assertEqual(0, add_ons[1].current_billing_cycle)
-
-        self.assertEqual(2, len(subscription.discounts))
-        discounts = sorted(subscription.discounts, key=lambda discount: discount.id)
-
-        self.assertEqual("discount_11", discounts[0].id)
-        self.assertEqual(Decimal("11.00"), discounts[0].amount)
-        self.assertEqual(1, discounts[0].quantity)
-        self.assertEqual(None, discounts[0].number_of_billing_cycles)
-        self.assertTrue(discounts[0].never_expires)
-        self.assertEqual(0, discounts[0].current_billing_cycle)
-
-        self.assertEqual("discount_7", discounts[1].id)
-        self.assertEqual(Decimal("15.00"), discounts[1].amount)
-        self.assertEqual(3, discounts[1].quantity)
-        self.assertEqual(19, discounts[1].number_of_billing_cycles)
-        self.assertFalse(discounts[1].never_expires)
-        self.assertEqual(0, discounts[1].current_billing_cycle)
-
-    def test_create_allows_deleting_of_inherited_add_ons_and_discounts(self):
-        subscription = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.add_on_discount_plan["id"],
-            "add_ons": {
-                "remove": ["increase_10", "increase_20"]
-            },
-            "discounts": {
-                "remove": ["discount_7"]
-            }
-        }).subscription
-
-        self.assertEqual(0, len(subscription.add_ons))
-        self.assertEqual(1, len(subscription.discounts))
-        self.assertEqual("discount_11", subscription.discounts[0].id)
-
-    def test_create_allows_adding_add_ons_and_discounts(self):
-        subscription = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.add_on_discount_plan["id"],
-            "add_ons": {
-                "add": [
-                    {
-                        "amount": Decimal("50.00"),
-                        "inherited_from_id": "increase_30",
-                        "quantity": 2,
-                        "number_of_billing_cycles": 5
-                    }
-                ],
-                "remove": ["increase_10", "increase_20"]
-            },
-            "discounts": {
-                "add": [
-                    {
-                        "amount": Decimal("17.00"),
-                        "inherited_from_id": "discount_15",
-                        "never_expires": True
-                    }
-                ],
-                "remove": ["discount_7", "discount_11"]
-            }
-        }).subscription
-
-        self.assertEqual(1, len(subscription.add_ons))
-
-        self.assertEqual("increase_30", subscription.add_ons[0].id)
-        self.assertEqual(Decimal("50.00"), subscription.add_ons[0].amount)
-        self.assertEqual(2, subscription.add_ons[0].quantity)
-        self.assertEqual(5, subscription.add_ons[0].number_of_billing_cycles)
-        self.assertFalse(subscription.add_ons[0].never_expires)
-        self.assertEqual(0, subscription.add_ons[0].current_billing_cycle)
-
-        self.assertEqual(1, len(subscription.discounts))
-
-        self.assertEqual("discount_15", subscription.discounts[0].id)
-        self.assertEqual(Decimal("17.00"), subscription.discounts[0].amount)
-        self.assertEqual(1, subscription.discounts[0].quantity)
-        self.assertEqual(None, subscription.discounts[0].number_of_billing_cycles)
-        self.assertTrue(subscription.discounts[0].never_expires)
-        self.assertEqual(0, subscription.discounts[0].current_billing_cycle)
-
-    def test_create_properly_parses_validation_errors_for_arrays(self):
-        result = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.add_on_discount_plan["id"],
-            "add_ons": {
-                "update": [
-                    {
-                        "existing_id": "increase_10",
-                        "amount": "invalid"
-                    },
-                    {
-                        "existing_id": "increase_20",
-                        "quantity": -2
-                    }
-                ]
-            }
-        })
-        self.assertFalse(result.is_success)
-
-        self.assertEqual(
-            ErrorCodes.Subscription.Modification.AmountIsInvalid,
-            result.errors.for_object("subscription").for_object("add_ons").for_object("update").for_index(0).on("amount")[0].code
-        )
-        self.assertEqual(
-            ErrorCodes.Subscription.Modification.QuantityIsInvalid,
-            result.errors.for_object("subscription").for_object("add_ons").for_object("update").for_index(1).on("quantity")[0].code
-        )
-
-    def test_descriptors_accepts_name_phone_and_url(self):
-        result = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.trialless_plan["id"],
-            "descriptor": {
-                "name": "123*123456789012345678",
-                "phone": "3334445555",
-                "url": "ebay.com"
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        subscription = result.subscription
-        self.assertEqual("123*123456789012345678", subscription.descriptor.name)
-        self.assertEqual("3334445555", subscription.descriptor.phone)
-
-        transaction = subscription.transactions[0]
-        self.assertEqual("123*123456789012345678", transaction.descriptor.name)
-        self.assertEqual("3334445555", transaction.descriptor.phone)
-        self.assertEqual("ebay.com", transaction.descriptor.url)
-
-    def test_descriptors_has_validation_errors_if_format_is_invalid(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            },
-            "descriptor": {
-                "name": "badcompanyname12*badproduct12",
-                "phone": "%bad4445555"
-            }
-        })
-        self.assertFalse(result.is_success)
-
-        name_errors = result.errors.for_object("transaction").for_object("descriptor").on("name")
-        self.assertEqual(1, len(name_errors))
-        self.assertEqual(ErrorCodes.Descriptor.NameFormatIsInvalid, name_errors[0].code)
-
-        phone_errors = result.errors.for_object("transaction").for_object("descriptor").on("phone")
-        self.assertEqual(1, len(phone_errors))
-        self.assertEqual(ErrorCodes.Descriptor.PhoneFormatIsInvalid, phone_errors[0].code)
-
-    def test_description(self):
-        http = ClientApiHttp.create()
-        status_code, nonce = http.get_paypal_nonce({
-            "consent-code": "consent-code",
-            "options": {"validate": False}
-        })
-        self.assertEqual(202, status_code)
-
-        payment_method_token = PaymentMethod.create({
-            "customer_id": Customer.create().customer.id,
-            "payment_method_nonce": nonce
-        }).payment_method.token
-
-        result = Subscription.create({
-            "payment_method_token": payment_method_token,
-            "plan_id": TestHelper.trialless_plan["id"],
-            "options": {
-                "paypal": {
-                    "description": "A great product"
-                }
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        subscription = result.subscription
-        self.assertEqual("A great product", subscription.description)
-
-        transaction = subscription.transactions[0]
-        paypal_details = transaction.paypal_details
-        self.assertEqual("A great product", paypal_details.description)
-
-    def test_find_with_valid_id(self):
-        subscription = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.trial_plan["id"],
-        }).subscription
-
-        found_subscription = Subscription.find(subscription.id)
-        self.assertEqual(subscription.id, found_subscription.id)
-
-    @raises_with_regexp(NotFoundError, "subscription with id 'bad_token' not found")
-    def test_find_with_invalid_token(self):
-        Subscription.find("bad_token")
-
-    def test_update_creates_a_prorated_transaction_when_merchant_is_set_to_prorate(self):
-        result = Subscription.update(self.updateable_subscription.id, {
-            "price": self.updateable_subscription.price + Decimal("1"),
-        })
-
-        self.assertTrue(result.is_success)
-
-        subscription = result.subscription
-        self.assertEqual(2, len(subscription.transactions))
-
-    def test_update_creates_a_prorated_transaction_when_flag_is_passed_as_True(self):
-        result = Subscription.update(self.updateable_subscription.id, {
-            "price": self.updateable_subscription.price + Decimal("1"),
-            "options": {
-                "prorate_charges": True
-            }
-        })
-
-        self.assertTrue(result.is_success)
-
-        subscription = result.subscription
-        self.assertEqual(2, len(subscription.transactions))
-
-    def test_update_does_not_create_a_prorated_transaction_when_flag_is_passed_as_False(self):
-        result = Subscription.update(self.updateable_subscription.id, {
-            "price": self.updateable_subscription.price + Decimal("1"),
-            "options": {
-                "prorate_charges": False
-            }
-        })
-
-        self.assertTrue(result.is_success)
-
-        subscription = result.subscription
-        self.assertEqual(1, len(subscription.transactions))
-
-    def test_update_does_not_update_subscription_when_revert_subscription_on_proration_failure_is_true(self):
-        result = Subscription.update(self.updateable_subscription.id, {
-            "price": self.updateable_subscription.price + Decimal("2100"),
-            "options": {
-                "prorate_charges": True,
-                "revert_subscription_on_proration_failure": True
-            }
-        })
-
-        self.assertFalse(result.is_success)
-
-        found_subscription = Subscription.find(result.subscription.id)
-        self.assertEqual(len(self.updateable_subscription.transactions) + 1, len(result.subscription.transactions))
-        self.assertEqual("processor_declined", result.subscription.transactions[0].status)
-
-        self.assertEqual(Decimal("0.00"), found_subscription.balance)
-        self.assertEqual(self.updateable_subscription.price, found_subscription.price)
-
-    def test_update_updates_subscription_when_revert_subscription_on_proration_failure_is_false(self):
-        result = Subscription.update(self.updateable_subscription.id, {
-            "price": self.updateable_subscription.price + Decimal("2100"),
-            "options": {
-                "prorate_charges": True,
-                "revert_subscription_on_proration_failure": False
-            }
-        })
-
-        self.assertTrue(result.is_success)
-
-        found_subscription = Subscription.find(result.subscription.id)
-        self.assertEqual(len(self.updateable_subscription.transactions) + 1, len(result.subscription.transactions))
-        self.assertEqual("processor_declined", result.subscription.transactions[0].status)
-
-        self.assertEqual(result.subscription.transactions[0].amount, Decimal(found_subscription.balance))
-        self.assertEqual(self.updateable_subscription.price + Decimal("2100"), found_subscription.price)
-
-    def test_update_with_successful_result(self):
-        new_id = str(random.randint(1, 1000000))
-        result = Subscription.update(self.updateable_subscription.id, {
-            "id": new_id,
-            "price": Decimal("9999.88"),
-            "plan_id": TestHelper.trial_plan["id"]
-        })
-
-        self.assertTrue(result.is_success)
-
-        subscription = result.subscription
-        self.assertEqual(new_id, subscription.id)
-        self.assertEqual(TestHelper.trial_plan["id"], subscription.plan_id)
-        self.assertEqual(Decimal("9999.88"), subscription.price)
-
-    def test_update_with_merchant_account_id(self):
-        result = Subscription.update(self.updateable_subscription.id, {
-            "merchant_account_id": TestHelper.non_default_merchant_account_id,
-        })
-
-        self.assertTrue(result.is_success)
-
-        subscription = result.subscription
-        self.assertEqual(TestHelper.non_default_merchant_account_id, subscription.merchant_account_id)
-
-    def test_update_with_payment_method_token(self):
-        newCard = CreditCard.create({
-            "customer_id": self.credit_card.customer_id,
-            "number": "4111111111111111",
-            "expiration_date": "05/2009",
-            "cvv": "100",
-            "cardholder_name": self.credit_card.cardholder_name
-        }).credit_card
-
-        result = Subscription.update(self.updateable_subscription.id, {
-            "payment_method_token": newCard.token
-        })
-
-        self.assertTrue(result.is_success)
-
-        subscription = result.subscription
-        self.assertEqual(newCard.token, subscription.payment_method_token)
-
-    def test_update_with_payment_method_nonce(self):
-        config = Configuration.instantiate()
-        customer_id = self.credit_card.customer_id
-        parsed_client_token = TestHelper.generate_decoded_client_token({"customer_id": customer_id})
-        authorization_fingerprint = json.loads(parsed_client_token)["authorizationFingerprint"]
-        http = ClientApiHttp(config, {
-            "authorization_fingerprint": authorization_fingerprint,
-            "shared_customer_identifier": "fake_identifier",
-            "shared_customer_identifier_type": "testing"
-        })
-        _, response = http.add_card({
-            "credit_card": {
-                "number": "4242424242424242",
-                "expiration_month": "11",
-                "expiration_year": "2099",
-            },
-            "share": True
-        })
-        nonce = json.loads(response)["creditCards"][0]["nonce"]
-
-        result = Subscription.update(self.updateable_subscription.id, {
-            "payment_method_nonce": nonce
-        })
-
-        self.assertTrue(result.is_success)
-
-        subscription = result.subscription
-        newCard = CreditCard.find(subscription.payment_method_token)
-        self.assertEqual("4242", newCard.last_4)
-        self.assertNotEqual(newCard.last_4, self.credit_card.last_4)
-
-    def test_update_with_number_of_billing_cycles(self):
-        result = Subscription.update(self.updateable_subscription.id, {
-            "number_of_billing_cycles": 10
-        })
-
-        self.assertTrue(result.is_success)
-
-        subscription = result.subscription
-        self.assertEqual(10, subscription.number_of_billing_cycles)
-
-    def test_update_with_never_expires(self):
-        result = Subscription.update(self.updateable_subscription.id, {
-            "never_expires": True
-        })
-
-        self.assertTrue(result.is_success)
-
-        subscription = result.subscription
-        self.assertEqual(None, subscription.number_of_billing_cycles)
-
-    def test_update_with_error_result(self):
-        result = Subscription.update(self.updateable_subscription.id, {
-            "id": "bad id",
-        })
-        self.assertFalse(result.is_success)
-
-        id_errors = result.errors.for_object("subscription").on("id")
-        self.assertEqual(1, len(id_errors))
-        self.assertEqual("81906", id_errors[0].code)
-
-    @raises(NotFoundError)
-    def test_update_raises_error_when_subscription_not_found(self):
-        Subscription.update("notfound", {
-            "id": "newid",
-        })
-
-    def test_update_allows_overriding_of_inherited_add_ons_and_discounts(self):
-        subscription = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.add_on_discount_plan["id"],
-        }).subscription
-
-        subscription = Subscription.update(subscription.id, {
-            "add_ons": {
-                "update": [
-                    {
-                        "amount": Decimal("50.00"),
-                        "existing_id": "increase_10",
-                        "quantity": 2,
-                        "number_of_billing_cycles": 5
-                    },
-                    {
-                        "amount": Decimal("100.00"),
-                        "existing_id": "increase_20",
-                        "quantity": 4,
-                        "never_expires": True
-                    }
-                ]
-            },
-            "discounts": {
-                "update": [
-                    {
-                        "amount": Decimal("15.00"),
-                        "existing_id": "discount_7",
-                        "quantity": 3,
-                        "number_of_billing_cycles": 19
-                    }
-                ]
-            }
-        }).subscription
-
-        self.assertEqual(2, len(subscription.add_ons))
-        add_ons = sorted(subscription.add_ons, key=lambda add_on: add_on.id)
-
-        self.assertEqual("increase_10", add_ons[0].id)
-        self.assertEqual(Decimal("50.00"), add_ons[0].amount)
-        self.assertEqual(2, add_ons[0].quantity)
-        self.assertEqual(5, add_ons[0].number_of_billing_cycles)
-        self.assertFalse(add_ons[0].never_expires)
-
-        self.assertEqual("increase_20", add_ons[1].id)
-        self.assertEqual(Decimal("100.00"), add_ons[1].amount)
-        self.assertEqual(4, add_ons[1].quantity)
-        self.assertEqual(None, add_ons[1].number_of_billing_cycles)
-        self.assertTrue(add_ons[1].never_expires)
-
-        self.assertEqual(2, len(subscription.discounts))
-        discounts = sorted(subscription.discounts, key=lambda discount: discount.id)
-
-        self.assertEqual("discount_11", discounts[0].id)
-        self.assertEqual(Decimal("11.00"), discounts[0].amount)
-        self.assertEqual(1, discounts[0].quantity)
-        self.assertEqual(None, discounts[0].number_of_billing_cycles)
-        self.assertTrue(discounts[0].never_expires)
-
-        self.assertEqual("discount_7", discounts[1].id)
-        self.assertEqual(Decimal("15.00"), discounts[1].amount)
-        self.assertEqual(3, discounts[1].quantity)
-        self.assertEqual(19, discounts[1].number_of_billing_cycles)
-        self.assertFalse(discounts[1].never_expires)
-
-    def test_update_allows_adding_and_removing_add_ons_and_discounts(self):
-        subscription = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.add_on_discount_plan["id"],
-        }).subscription
-
-        subscription = Subscription.update(subscription.id, {
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.add_on_discount_plan["id"],
-            "add_ons": {
-                "add": [
-                    {
-                        "amount": Decimal("50.00"),
-                        "inherited_from_id": "increase_30",
-                        "quantity": 2,
-                        "number_of_billing_cycles": 5
-                    }
-                ],
-                "remove": ["increase_10", "increase_20"]
-            },
-            "discounts": {
-                "add": [
-                    {
-                        "amount": Decimal("17.00"),
-                        "inherited_from_id": "discount_15",
-                        "never_expires": True
-                    }
-                ],
-                "remove": ["discount_7", "discount_11"]
-            }
-        }).subscription
-
-        self.assertEqual(1, len(subscription.add_ons))
-
-        self.assertEqual("increase_30", subscription.add_ons[0].id)
-        self.assertEqual(Decimal("50.00"), subscription.add_ons[0].amount)
-        self.assertEqual(2, subscription.add_ons[0].quantity)
-        self.assertEqual(5, subscription.add_ons[0].number_of_billing_cycles)
-        self.assertFalse(subscription.add_ons[0].never_expires)
-
-        self.assertEqual(1, len(subscription.discounts))
-
-        self.assertEqual("discount_15", subscription.discounts[0].id)
-        self.assertEqual(Decimal("17.00"), subscription.discounts[0].amount)
-        self.assertEqual(1, subscription.discounts[0].quantity)
-        self.assertEqual(None, subscription.discounts[0].number_of_billing_cycles)
-        self.assertTrue(subscription.discounts[0].never_expires)
-
-    def test_update_allows_adding_and_removing_unicode_add_ons_and_discounts(self):
-        subscription = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.add_on_discount_plan["id"],
-        }).subscription
-
-        subscription = Subscription.update(subscription.id, {
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.add_on_discount_plan["id"],
-            "add_ons": {
-                "add": [
-                    {
-                        "amount": Decimal("50.00"),
-                        "inherited_from_id": u"increase_30",
-                        "quantity": 2,
-                        "number_of_billing_cycles": 5
-                    }
-                ],
-                "remove": [u"increase_10", u"increase_20"]
-            },
-            "discounts": {
-                "add": [
-                    {
-                        "amount": Decimal("17.00"),
-                        "inherited_from_id": u"discount_15",
-                        "never_expires": True
-                    }
-                ],
-                "remove": [u"discount_7", u"discount_11"]
-            }
-        }).subscription
-
-        self.assertEqual(1, len(subscription.add_ons))
-
-        self.assertEqual(u"increase_30", subscription.add_ons[0].id)
-        self.assertEqual(Decimal("50.00"), subscription.add_ons[0].amount)
-        self.assertEqual(2, subscription.add_ons[0].quantity)
-        self.assertEqual(5, subscription.add_ons[0].number_of_billing_cycles)
-        self.assertFalse(subscription.add_ons[0].never_expires)
-
-        self.assertEqual(1, len(subscription.discounts))
-
-        self.assertEqual(u"discount_15", subscription.discounts[0].id)
-        self.assertEqual(Decimal("17.00"), subscription.discounts[0].amount)
-        self.assertEqual(1, subscription.discounts[0].quantity)
-        self.assertEqual(None, subscription.discounts[0].number_of_billing_cycles)
-        self.assertTrue(subscription.discounts[0].never_expires)
-
-    def test_update_can_replace_entire_set_of_add_ons_and_discounts(self):
-        subscription = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.add_on_discount_plan["id"],
-        }).subscription
-
-        subscription = Subscription.update(subscription.id, {
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.add_on_discount_plan["id"],
-            "add_ons": {
-                "add": [
-                    {"inherited_from_id": "increase_30"},
-                    {"inherited_from_id": "increase_20"},
-                ],
-            },
-            "discounts": {
-                "add": [
-                    {"inherited_from_id": "discount_15"},
-                ],
-            },
-            "options": {
-                "replace_all_add_ons_and_discounts": True,
-            },
-        }).subscription
-
-        self.assertEqual(2, len(subscription.add_ons))
-        add_ons = sorted(subscription.add_ons, key=lambda add_on: add_on.id)
-
-        self.assertEqual("increase_20", add_ons[0].id)
-        self.assertEqual(Decimal("20.00"), add_ons[0].amount)
-        self.assertEqual(1, add_ons[0].quantity)
-        self.assertEqual(None, add_ons[0].number_of_billing_cycles)
-        self.assertTrue(add_ons[0].never_expires)
-
-        self.assertEqual("increase_30", add_ons[1].id)
-        self.assertEqual(Decimal("30.00"), add_ons[1].amount)
-        self.assertEqual(1, add_ons[1].quantity)
-        self.assertEqual(None, add_ons[1].number_of_billing_cycles)
-        self.assertTrue(add_ons[1].never_expires)
-
-        self.assertEqual(1, len(subscription.discounts))
-
-        self.assertEqual("discount_15", subscription.discounts[0].id)
-        self.assertEqual(Decimal("15.00"), subscription.discounts[0].amount)
-        self.assertEqual(1, subscription.discounts[0].quantity)
-        self.assertEqual(None, subscription.discounts[0].number_of_billing_cycles)
-        self.assertTrue(subscription.discounts[0].never_expires)
-
-    def test_update_descriptor_name_and_phone(self):
-        result = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.trialless_plan["id"],
-            "descriptor": {
-                "name": "123*123456789012345678",
-                "phone": "1234567890"
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        subscription = result.subscription
-        updated_subscription = Subscription.update(subscription.id, {
-            "descriptor": {
-                "name": "999*99",
-                "phone": "1234567890"
-            }
-        }).subscription
-
-        self.assertEqual("999*99", updated_subscription.descriptor.name)
-        self.assertEqual("1234567890", updated_subscription.descriptor.phone)
-
-    def test_update_description(self):
-        http = ClientApiHttp.create()
-        status_code, nonce = http.get_paypal_nonce({
-            "consent-code": "consent-code",
-            "options": {"validate": False}
-        })
-        self.assertEqual(202, status_code)
-
-        payment_method_token = PaymentMethod.create({
-            "customer_id": Customer.create().customer.id,
-            "payment_method_nonce": nonce
-        }).payment_method.token
-
-        result = Subscription.create({
-            "payment_method_token": payment_method_token,
-            "plan_id": TestHelper.trialless_plan["id"],
-            "options": {
-                "paypal": {
-                    "description": "A great product"
-                }
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        subscription = result.subscription
-        updated_subscription = Subscription.update(subscription.id, {
-            "options": {
-                "paypal": {
-                    "description": "An incredible product"
-                }
-            }
-        }).subscription
-
-        self.assertEqual("An incredible product", updated_subscription.description)
-
-    def test_cancel_with_successful_response(self):
-        subscription = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.trialless_plan["id"]
-        }).subscription
-
-        result = Subscription.cancel(subscription.id)
-        self.assertTrue(result.is_success)
-        self.assertEqual("Canceled", result.subscription.status)
-
-    def test_unsuccessful_cancel_returns_validation_error(self):
-        Subscription.cancel(self.updateable_subscription.id)
-        result = Subscription.cancel(self.updateable_subscription.id)
-        self.assertFalse(result.is_success)
-
-        status_errors = result.errors.for_object("subscription").on("status")
-        self.assertTrue(len(status_errors), 1)
-        self.assertEqual("81905", status_errors[0].code)
-
-    @raises(NotFoundError)
-    def test_cancel_raises_not_found_error_with_bad_subscription(self):
-        Subscription.cancel("notreal")
-
-    def test_search_with_argument_list_rather_than_literal_list(self):
-        trial_subscription = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.trial_plan["id"],
-            "price": Decimal("1")
-        }).subscription
-
-        trialless_subscription = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.trialless_plan["id"],
-            "price": Decimal("1")
-        }).subscription
-
-        collection = Subscription.search(
-            SubscriptionSearch.plan_id == "integration_trial_plan",
-            SubscriptionSearch.price == Decimal("1")
-        )
-
-        self.assertTrue(TestHelper.includes(collection, trial_subscription))
-        self.assertFalse(TestHelper.includes(collection, trialless_subscription))
-
-    def test_search_on_billing_cycles_remaining(self):
-        subscription_5 = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.trial_plan["id"],
-            "number_of_billing_cycles": 5
-        }).subscription
-
-        subscription_10 = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.trial_plan["id"],
-            "number_of_billing_cycles": 10
-        }).subscription
-
-        collection = Subscription.search([
-            SubscriptionSearch.billing_cycles_remaining >= 7
-        ])
-
-        self.assertTrue(TestHelper.includes(collection, subscription_10))
-        self.assertFalse(TestHelper.includes(collection, subscription_5))
-
-    def test_search_on_created_at(self):
-        subscription = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.trialless_plan["id"],
-        }).subscription
-
-        empty_collection = Subscription.search([
-            SubscriptionSearch.created_at.between(date.today() + timedelta(1), date.today() + timedelta(2))
-        ])
-
-        self.assertTrue(empty_collection.maximum_size == 0)
-
-        success_collection = Subscription.search([
-            SubscriptionSearch.created_at.between(date.today() - timedelta(1), date.today() + timedelta(1))
-        ])
-
-        self.assertTrue(success_collection.maximum_size > 0)
-
-    def test_search_on_days_past_due(self):
-        subscription = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.trialless_plan["id"],
-        }).subscription
-        TestHelper.make_past_due(subscription, 3)
-
-        collection = Subscription.search([
-            SubscriptionSearch.days_past_due.between(2, 10)
-        ])
-
-        self.assertTrue(collection.maximum_size > 0)
-        for subscription in collection.items:
-            self.assertTrue(2 <= subscription.days_past_due <= 10)
-
-    def test_search_on_plan_id(self):
-        trial_subscription = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.trial_plan["id"],
-            "price": Decimal("2")
-        }).subscription
-
-        trialless_subscription = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.trialless_plan["id"],
-            "price": Decimal("2")
-        }).subscription
-
-        collection = Subscription.search([
-            SubscriptionSearch.plan_id == "integration_trial_plan",
-            SubscriptionSearch.price == Decimal("2")
-        ])
-
-        self.assertTrue(TestHelper.includes(collection, trial_subscription))
-        self.assertFalse(TestHelper.includes(collection, trialless_subscription))
-
-        collection = Subscription.search([
-            SubscriptionSearch.plan_id.in_list("integration_trial_plan", "integration_trialless_plan"),
-            SubscriptionSearch.price == Decimal("2")
-        ])
-
-        self.assertTrue(TestHelper.includes(collection, trial_subscription))
-        self.assertTrue(TestHelper.includes(collection, trialless_subscription))
-
-    def test_search_on_plan_id_is_acts_like_text_node_instead_of_multiple_value(self):
-        for plan in [TestHelper.trial_plan, TestHelper.trialless_plan]:
-            Subscription.create({
-                "payment_method_token": self.credit_card.token,
-                "plan_id": plan["id"],
-                "price": Decimal("3")
-            })
-
-        collection = Subscription.search([
-            SubscriptionSearch.plan_id == "no such plan id",
-            SubscriptionSearch.price == Decimal("3")
-        ])
-
-        self.assertEqual(0, collection.maximum_size)
-
-    def test_search_on_status(self):
-        active_subscription = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.trialless_plan["id"],
-            "price": Decimal("3")
-        }).subscription
-
-        canceled_subscription = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.trialless_plan["id"],
-            "price": Decimal("3")
-        }).subscription
-        Subscription.cancel(canceled_subscription.id)
-
-        collection = Subscription.search([
-            SubscriptionSearch.status.in_list([Subscription.Status.Active, Subscription.Status.Canceled]),
-            SubscriptionSearch.price == Decimal("3")
-        ])
-
-        self.assertTrue(TestHelper.includes(collection, active_subscription))
-        self.assertTrue(TestHelper.includes(collection, canceled_subscription))
-
-    def test_search_on_merchant_account_id(self):
-        subscription_default_ma = Subscription.create({
-            "merchant_account_id": TestHelper.default_merchant_account_id,
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.trial_plan["id"],
-            "price": Decimal("4")
-        }).subscription
-
-        subscription_non_default_ma = Subscription.create({
-            "merchant_account_id": TestHelper.non_default_merchant_account_id,
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.trial_plan["id"],
-            "price": Decimal("4")
-        }).subscription
-
-        collection = Subscription.search([
-            SubscriptionSearch.merchant_account_id == TestHelper.default_merchant_account_id,
-            SubscriptionSearch.price == Decimal("4")
-        ])
-
-        self.assertTrue(TestHelper.includes(collection, subscription_default_ma))
-        self.assertFalse(TestHelper.includes(collection, subscription_non_default_ma))
-
-    def test_search_on_bogus_merchant_account_id(self):
-        subscription = Subscription.create({
-            "merchant_account_id": TestHelper.default_merchant_account_id,
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.trial_plan["id"],
-            "price": Decimal("4")
-        }).subscription
-
-        collection = Subscription.search([
-            SubscriptionSearch.merchant_account_id == subscription.merchant_account_id,
-            SubscriptionSearch.price == Decimal("4")
-        ])
-
-        self.assertTrue(TestHelper.includes(collection, subscription))
-
-        collection = Subscription.search([
-            SubscriptionSearch.merchant_account_id.in_list(["totally_bogus_id", subscription.merchant_account_id]),
-            SubscriptionSearch.price == Decimal("4")
-        ])
-
-        self.assertTrue(TestHelper.includes(collection, subscription))
-
-        collection = Subscription.search([
-            SubscriptionSearch.merchant_account_id == "totally_bogus_id",
-            SubscriptionSearch.price == Decimal("4")
-        ])
-
-        self.assertFalse(TestHelper.includes(collection, subscription))
-
-    def test_search_on_price(self):
-        subscription_900 = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.trial_plan["id"],
-            "price": Decimal("900")
-        }).subscription
-
-        subscription_1000 = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.trial_plan["id"],
-            "price": Decimal("1000")
-        }).subscription
-
-        collection = Subscription.search([
-            SubscriptionSearch.price >= Decimal("950")
-        ])
-
-        self.assertTrue(TestHelper.includes(collection, subscription_1000))
-        self.assertFalse(TestHelper.includes(collection, subscription_900))
-
-    def test_search_on_transaction_id(self):
-        subscription_found = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.trialless_plan["id"],
-        }).subscription
-
-        subscription_not_found = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.trialless_plan["id"],
-        }).subscription
-
-        collection = Subscription.search(
-            SubscriptionSearch.transaction_id == subscription_found.transactions[0].id
-        )
-
-        self.assertTrue(TestHelper.includes(collection, subscription_found))
-        self.assertFalse(TestHelper.includes(collection, subscription_not_found))
-
-    def test_search_on_id(self):
-        subscription_found = Subscription.create({
-            "id": "find_me_%s" % random.randint(1, 1000000),
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.trial_plan["id"],
-        }).subscription
-
-        subscription_not_found = Subscription.create({
-            "id": "do_not_find_me_%s" % random.randint(1, 1000000),
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.trial_plan["id"],
-        }).subscription
-
-        collection = Subscription.search([
-            SubscriptionSearch.id.starts_with("find_me")
-        ])
-
-        self.assertTrue(TestHelper.includes(collection, subscription_found))
-        self.assertFalse(TestHelper.includes(collection, subscription_not_found))
-
-    def test_search_on_next_billing_date(self):
-        subscription_found = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.trialless_plan["id"]
-        }).subscription
-
-        subscription_not_found = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.trial_plan["id"]
-        }).subscription
-
-        next_billing_date_cutoff = datetime.today() + timedelta(days=5)
-
-        collection = Subscription.search(
-            SubscriptionSearch.next_billing_date >= next_billing_date_cutoff
-        )
-
-        self.assertTrue(TestHelper.includes(collection, subscription_found))
-        self.assertFalse(TestHelper.includes(collection, subscription_not_found))
-
-    def test_retryCharge_without_amount__deprecated(self):
-        subscription = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.trialless_plan["id"],
-        }).subscription
-        TestHelper.make_past_due(subscription)
-
-        result = Subscription.retryCharge(subscription.id)
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-
-        self.assertEqual(subscription.price, transaction.amount)
-        self.assertNotEqual(None, transaction.processor_authorization_code)
-        self.assertEqual(Transaction.Type.Sale, transaction.type)
-        self.assertEqual(Transaction.Status.Authorized, transaction.status)
-
-    def test_retry_charge_without_amount(self):
-        subscription = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.trialless_plan["id"],
-        }).subscription
-        TestHelper.make_past_due(subscription)
-
-        result = Subscription.retry_charge(subscription.id)
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-
-        self.assertEqual(subscription.price, transaction.amount)
-        self.assertNotEqual(None, transaction.processor_authorization_code)
-        self.assertEqual(Transaction.Type.Sale, transaction.type)
-        self.assertEqual(Transaction.Status.Authorized, transaction.status)
-
-    def test_retryCharge_with_amount__deprecated(self):
-        subscription = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.trialless_plan["id"],
-        }).subscription
-        TestHelper.make_past_due(subscription)
-
-        result = Subscription.retryCharge(subscription.id, Decimal(TransactionAmounts.Authorize))
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-
-        self.assertEqual(Decimal(TransactionAmounts.Authorize), transaction.amount)
-        self.assertNotEqual(None, transaction.processor_authorization_code)
-        self.assertEqual(Transaction.Type.Sale, transaction.type)
-        self.assertEqual(Transaction.Status.Authorized, transaction.status)
-
-    def test_retry_charge_with_amount(self):
-        subscription = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.trialless_plan["id"],
-        }).subscription
-        TestHelper.make_past_due(subscription)
-
-        result = Subscription.retry_charge(subscription.id, Decimal(TransactionAmounts.Authorize))
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-
-        self.assertEqual(Decimal(TransactionAmounts.Authorize), transaction.amount)
-        self.assertNotEqual(None, transaction.processor_authorization_code)
-        self.assertEqual(Transaction.Type.Sale, transaction.type)
-        self.assertEqual(Transaction.Status.Authorized, transaction.status)
-
-    def test_retry_charge_with_submit_for_settlement(self):
-        subscription = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.trialless_plan["id"],
-        }).subscription
-        TestHelper.make_past_due(subscription)
-
-        result = Subscription.retry_charge(subscription.id, None, True)
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-
-        self.assertEqual(subscription.price, transaction.amount)
-        self.assertNotEqual(None, transaction.processor_authorization_code)
-        self.assertEqual(Transaction.Type.Sale, transaction.type)
-        self.assertEqual(Transaction.Status.SubmittedForSettlement, transaction.status)
-
-    def test_retry_charge_with_submit_for_settlement_and_amount(self):
-        subscription = Subscription.create({
-            "payment_method_token": self.credit_card.token,
-            "plan_id": TestHelper.trialless_plan["id"],
-        }).subscription
-        TestHelper.make_past_due(subscription)
-
-        result = Subscription.retry_charge(subscription.id, Decimal(TransactionAmounts.Authorize), True)
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-
-        self.assertEqual(Decimal(TransactionAmounts.Authorize), transaction.amount)
-        self.assertNotEqual(None, transaction.processor_authorization_code)
-        self.assertEqual(Transaction.Type.Sale, transaction.type)
-        self.assertEqual(Transaction.Status.SubmittedForSettlement, transaction.status)
-
-    def test_create_with_paypal_future_payment_method_token(self):
-        http = ClientApiHttp.create()
-        status_code, nonce = http.get_paypal_nonce({
-            "consent-code": "consent-code",
-            "options": {"validate": False}
-        })
-        self.assertEqual(202, status_code)
-
-        payment_method_token = PaymentMethod.create({
-            "customer_id": Customer.create().customer.id,
-            "payment_method_nonce": nonce
-        }).payment_method.token
-
-        result = Subscription.create({
-            "payment_method_token": payment_method_token,
-            "plan_id": TestHelper.trialless_plan["id"]
-        })
-
-        self.assertTrue(result.is_success)
-        subscription = result.subscription
-        self.assertEqual(payment_method_token, subscription.payment_method_token)
-
-    def test_create_fails_with_paypal_one_time_payment_method_nonce(self):
-        result = Subscription.create({
-            "payment_method_nonce": Nonces.PayPalOneTimePayment,
-            "plan_id": TestHelper.trialless_plan["id"]
-        })
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(
-            ErrorCodes.Subscription.PaymentMethodNonceIsInvalid,
-            result.errors.for_object("subscription")[0].code
-        )
-
-    def test_create_fails_with_paypal_future_payment_method_nonce(self):
-        result = Subscription.create({
-            "payment_method_nonce": Nonces.PayPalFuturePayment,
-            "plan_id": TestHelper.trialless_plan["id"]
-        })
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(
-            ErrorCodes.Subscription.PaymentMethodNonceIsInvalid,
-            result.errors.for_object("subscription")[0].code
-        )
diff --git a/tests/integration/test_test_helper.py b/tests/integration/test_test_helper.py
deleted file mode 100644
index 9c83241..0000000
--- a/tests/integration/test_test_helper.py
+++ /dev/null
@@ -1,28 +0,0 @@
-from tests.test_helper import *
-from braintree.test.nonces import Nonces
-
-class TestTestHelper(unittest.TestCase):
-    def setUp(self):
-        self.transaction = Transaction.sale({
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2010",
-                "cvv": "100"
-            },
-            "amount": "100.00",
-            "options": {
-                "submit_for_settlement": "true"
-            }
-        }).transaction
-
-    def test_settle_transaction_settles_transaction(self):
-        TestHelper.settle_transaction(self.transaction.id)
-        self.assertEqual(Transaction.Status.Settled, Transaction.find(self.transaction.id).status)
-
-    def test_settlement_confirm_transaction(self):
-        TestHelper.settlement_confirm_transaction(self.transaction.id)
-        self.assertEqual(Transaction.Status.SettlementConfirmed, Transaction.find(self.transaction.id).status)
-
-    def test_settlement_decline_transaction(self):
-        TestHelper.settlement_decline_transaction(self.transaction.id)
-        self.assertEqual(Transaction.Status.SettlementDeclined, Transaction.find(self.transaction.id).status)
diff --git a/tests/integration/test_testing_gateway.py b/tests/integration/test_testing_gateway.py
deleted file mode 100644
index 95e04b3..0000000
--- a/tests/integration/test_testing_gateway.py
+++ /dev/null
@@ -1,37 +0,0 @@
-from tests.test_helper import *
-from braintree.configuration import Configuration
-from braintree.exceptions.test_operation_performed_in_production_error import TestOperationPerformedInProductionError
-
-class TestTestingGateway(unittest.TestCase):
-    def setUp(self):
-        config = Configuration(braintree.Environment.Production, "merchant_id", "public_key", "private_key")
-        braintree_gateway = BraintreeGateway(config)
-        self.gateway = TestingGateway(braintree_gateway)
-
-    @raises(TestOperationPerformedInProductionError)
-    def test_error_is_raised_in_production_for_settle_transaction(self):
-        self.gateway.settle_transaction("")
-
-    @raises(TestOperationPerformedInProductionError)
-    def test_error_is_raised_in_production_for_make_past_due(self):
-        self.gateway.make_past_due("")
-
-    @raises(TestOperationPerformedInProductionError)
-    def test_error_is_raised_in_production_for_escrow_transaction(self):
-        self.gateway.escrow_transaction("")
-
-    @raises(TestOperationPerformedInProductionError)
-    def test_error_is_raised_in_production_for_settlement_confirm_transaction(self):
-        self.gateway.settlement_confirm_transaction("")
-
-    @raises(TestOperationPerformedInProductionError)
-    def test_error_is_raised_in_production_for_settlement_decline_transaction(self):
-        self.gateway.settlement_decline_transaction("")
-
-    @raises(TestOperationPerformedInProductionError)
-    def test_error_is_raised_in_production_for_create_3ds_verification(self):
-        self.gateway.create_3ds_verification("", "")
-
-    @raises(TestOperationPerformedInProductionError)
-    def test_error_is_raised_in_production(self):
-        self.gateway.settle_transaction("")
diff --git a/tests/integration/test_transaction.py b/tests/integration/test_transaction.py
deleted file mode 100644
index 9be33f7..0000000
--- a/tests/integration/test_transaction.py
+++ /dev/null
@@ -1,5537 +0,0 @@
-import json
-from tests.test_helper import *
-from braintree.test.credit_card_numbers import CreditCardNumbers
-from braintree.test.nonces import Nonces
-from braintree.dispute import Dispute
-from braintree.payment_instrument_type import PaymentInstrumentType
-
-import braintree.test.venmo_sdk as venmo_sdk
-
-class TestTransaction(unittest.TestCase):
-
-    def test_sale(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            },
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-        self.assertEqual("1000", transaction.processor_response_code)
-        self.assertEqual(ProcessorResponseTypes.Approved, transaction.processor_response_type)
-
-    def test_sale_returns_risk_data(self):
-        with AdvancedFraudIntegrationMerchant():
-            result = Transaction.sale({
-                "amount": TransactionAmounts.Authorize,
-                "credit_card": {
-                    "number": "4111111111111111",
-                    "expiration_date": "05/2009"
-                },
-                "device_session_id": "abc123",
-            })
-
-            self.assertTrue(result.is_success)
-            transaction = result.transaction
-            self.assertIsInstance(transaction.risk_data, RiskData)
-            self.assertNotEqual(transaction.risk_data.id, None)
-            self.assertEqual(transaction.risk_data.decision, "Approve")
-            self.assertTrue(hasattr(transaction.risk_data, 'device_data_captured'))
-
-    def test_sale_receives_network_transaction_id_visa(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": CreditCardNumbers.Visa,
-                "expiration_date": "05/2009"
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertTrue(len(result.transaction.network_transaction_id) > 0)
-
-    def test_case_accepts_previous_network_transaction_id_mastercard(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": CreditCardNumbers.MasterCard,
-                "expiration_date": "05/2009"
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertTrue(len(result.transaction.network_transaction_id) > 0)
-
-    def test_sale_does_not_accept_previous_network_transaction_id_elo(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": CreditCardNumbers.Elo,
-                "expiration_date": "05/2009"
-            }
-        })
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(result.transaction, None)
-
-    def test_sale_accepts_external_vault_status_visa(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": CreditCardNumbers.Visa,
-                "expiration_date": "05/2009"
-            },
-            "external_vault": {
-                "status": "will_vault",
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertNotEqual(result.transaction.network_transaction_id, "")
-
-    def test_sale_accepts_external_vault_status_mastercard(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": CreditCardNumbers.MasterCard,
-                "expiration_date": "05/2009"
-            },
-            "external_vault": {
-                "status": "will_vault",
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertTrue(len(result.transaction.network_transaction_id) > 0)
-
-    def test_sale_does_not_accept_external_vault_status_elo(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": CreditCardNumbers.Elo,
-                "expiration_date": "05/2009"
-            },
-            "external_vault": {
-                "status": "will_vault",
-            }
-        })
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(result.transaction, None)
-
-
-    def test_sale_accepts_blank_external_vault_previous_network_transaction_id_non_visa(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": CreditCardNumbers.MasterCard,
-                "expiration_date": "05/2009"
-            },
-            "external_vault": {
-                "status": "vaulted",
-                "previous_network_transaction_id": ""
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertTrue(len(result.transaction.network_transaction_id) > 0)
-
-    def test_sale_accepts_external_vault_status_vaulted_without_previous_network_transaction_id(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": CreditCardNumbers.Visa,
-                "expiration_date": "05/2009"
-            },
-            "external_vault": {
-                "status": "vaulted",
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertNotEqual(result.transaction.network_transaction_id, "")
-
-    def test_sale_accepts_external_vault_previous_network_transaction_id(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": CreditCardNumbers.Visa,
-                "expiration_date": "05/2009"
-            },
-            "external_vault": {
-                "status": "vaulted",
-                "previous_network_transaction_id": "123456789012345"
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertNotEqual(result.transaction.network_transaction_id, "")
-
-    def test_sale_with_external_vault_validation_error_unsupported_payment_instrument_type(self):
-        result = Transaction.sale({
-            "amount": "10.00",
-            "payment_method_nonce": Nonces.ApplePayVisa,
-            "external_vault": {
-                "status": "vaulted",
-                "previous_network_transaction_id": "123456789012345"
-            }
-        })
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(
-            ErrorCodes.Transaction.PaymentInstrumentWithExternalVaultIsInvalid,
-            result.errors.for_object("transaction")[0].code
-        )
-
-    def test_sale_with_external_vault_validation_error_invalid_status(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": CreditCardNumbers.Visa,
-                "expiration_date": "05/2009"
-            },
-            "external_vault": {
-                "status": "bad value"
-            }
-        })
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(
-            ErrorCodes.Transaction.ExternalVault.StatusIsInvalid,
-            result.errors.for_object("transaction").for_object("external_vault").on("status")[0].code
-        )
-
-    def test_sale_with_external_vault_validation_error_invalid_status_with_previous_network_transaction_id(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": CreditCardNumbers.Visa,
-                "expiration_date": "05/2009"
-            },
-            "external_vault": {
-                "status": "will_vault",
-                "previous_network_transaction_id": "123456789012345"
-            }
-        })
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(
-            ErrorCodes.Transaction.ExternalVault.StatusWithPreviousNetworkTransactionIdIsInvalid,
-            result.errors.for_object("transaction").for_object("external_vault").on("status")[0].code
-        )
-
-    def test_sale_with_external_vault_discover_success(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": CreditCardNumbers.Discover,
-                "expiration_date": "05/2009"
-            },
-            "external_vault": {
-                "status": "vaulted",
-                "previous_network_transaction_id": "123456789012345"
-            }
-        })
-
-        self.assertTrue(result.is_success)
-
-    def test_sale_with_external_vault_validation_error_invalid_card_type(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": CreditCardNumbers.Elo,
-                "expiration_date": "05/2009"
-            },
-            "external_vault": {
-                "status": "vaulted",
-                "previous_network_transaction_id": "123456789012345"
-            }
-        })
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(
-            ErrorCodes.Transaction.ExternalVault.CardTypeIsInvalid,
-            result.errors.for_object("transaction").for_object("external_vault").on("previous_network_transaction_id")[0].code
-        )
-
-    def test_sale_returns_a_successful_result_with_type_of_sale(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-        self.assertNotEqual(None, re.search(r"\A\w{6,}\Z", transaction.id))
-        self.assertEqual(Transaction.Type.Sale, transaction.type)
-        self.assertEqual(Decimal(TransactionAmounts.Authorize), transaction.amount)
-        self.assertEqual("411111", transaction.credit_card_details.bin)
-        self.assertEqual("1111", transaction.credit_card_details.last_4)
-        self.assertEqual("05/2009", transaction.credit_card_details.expiration_date)
-        self.assertEqual(None, transaction.voice_referral_number)
-
-    def test_sale_allows_amount_as_a_decimal(self):
-        result = Transaction.sale({
-            "amount": Decimal(TransactionAmounts.Authorize),
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-        self.assertNotEqual(None, re.search(r"\A\w{6,}\Z", transaction.id))
-        self.assertEqual(Transaction.Type.Sale, transaction.type)
-        self.assertEqual(Decimal(TransactionAmounts.Authorize), transaction.amount)
-        self.assertEqual("411111", transaction.credit_card_details.bin)
-        self.assertEqual("1111", transaction.credit_card_details.last_4)
-        self.assertEqual("05/2009", transaction.credit_card_details.expiration_date)
-
-    def test_sale_with_expiration_month_and_year_separately(self):
-        result = Transaction.sale({
-            "amount": Decimal(TransactionAmounts.Authorize),
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_month": "05",
-                "expiration_year": "2012"
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-        self.assertEqual(Transaction.Type.Sale, transaction.type)
-        self.assertEqual("05", transaction.credit_card_details.expiration_month)
-        self.assertEqual("2012", transaction.credit_card_details.expiration_year)
-
-    def test_sale_works_with_all_attributes(self):
-        result = Transaction.sale({
-            "amount": "100.00",
-            "order_id": "123",
-            "channel": "MyShoppingCartProvider",
-            "credit_card": {
-                "cardholder_name": "The Cardholder",
-                "number": "5105105105105100",
-                "expiration_date": "05/2011",
-                "cvv": "123"
-            },
-            "customer": {
-                "first_name": "Dan",
-                "last_name": "Smith",
-                "company": "Braintree",
-                "email": "dan@example.com",
-                "phone": "419-555-1234",
-                "fax": "419-555-1235",
-                "website": "http://braintreepayments.com"
-            },
-            "billing": {
-                "first_name": "Carl",
-                "last_name": "Jones",
-                "company": "Braintree",
-                "street_address": "123 E Main St",
-                "extended_address": "Suite 403",
-                "locality": "Chicago",
-                "region": "IL",
-                "postal_code": "60622",
-                "country_name": "United States of America",
-                "country_code_alpha2": "US",
-                "country_code_alpha3": "USA",
-                "country_code_numeric": "840"
-            },
-            "shipping": {
-                "first_name": "Andrew",
-                "last_name": "Mason",
-                "company": "Braintree",
-                "street_address": "456 W Main St",
-                "extended_address": "Apt 2F",
-                "locality": "Bartlett",
-                "region": "IL",
-                "postal_code": "60103",
-                "country_name": "Mexico",
-                "country_code_alpha2": "MX",
-                "country_code_alpha3": "MEX",
-                "country_code_numeric": "484"
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-        self.assertNotEqual(None, re.search(r"\A\w{6,}\Z", transaction.id))
-        self.assertEqual(Transaction.Type.Sale, transaction.type)
-        self.assertEqual(Transaction.Status.Authorized, transaction.status)
-        self.assertEqual(Decimal("100.00"), transaction.amount)
-        self.assertEqual("123", transaction.order_id)
-        self.assertEqual("MyShoppingCartProvider", transaction.channel)
-        self.assertEqual("1000", transaction.processor_response_code)
-        self.assertEqual(datetime, type(transaction.authorization_expires_at))
-        self.assertEqual(datetime, type(transaction.created_at))
-        self.assertEqual(datetime, type(transaction.updated_at))
-        self.assertEqual("510510", transaction.credit_card_details.bin)
-        self.assertEqual("5100", transaction.credit_card_details.last_4)
-        self.assertEqual("510510******5100", transaction.credit_card_details.masked_number)
-        self.assertEqual("MasterCard", transaction.credit_card_details.card_type)
-        self.assertEqual("The Cardholder", transaction.credit_card_details.cardholder_name)
-        self.assertEqual(None, transaction.avs_error_response_code)
-        self.assertEqual("M", transaction.avs_postal_code_response_code)
-        self.assertEqual("M", transaction.avs_street_address_response_code)
-        self.assertEqual("Dan", transaction.customer_details.first_name)
-        self.assertEqual("Smith", transaction.customer_details.last_name)
-        self.assertEqual("Braintree", transaction.customer_details.company)
-        self.assertEqual("dan@example.com", transaction.customer_details.email)
-        self.assertEqual("419-555-1234", transaction.customer_details.phone)
-        self.assertEqual("419-555-1235", transaction.customer_details.fax)
-        self.assertEqual("http://braintreepayments.com", transaction.customer_details.website)
-        self.assertEqual("Carl", transaction.billing_details.first_name)
-        self.assertEqual("Jones", transaction.billing_details.last_name)
-        self.assertEqual("Braintree", transaction.billing_details.company)
-        self.assertEqual("123 E Main St", transaction.billing_details.street_address)
-        self.assertEqual("Suite 403", transaction.billing_details.extended_address)
-        self.assertEqual("Chicago", transaction.billing_details.locality)
-        self.assertEqual("IL", transaction.billing_details.region)
-        self.assertEqual("60622", transaction.billing_details.postal_code)
-        self.assertEqual("United States of America", transaction.billing_details.country_name)
-        self.assertEqual("US", transaction.billing_details.country_code_alpha2)
-        self.assertEqual("USA", transaction.billing_details.country_code_alpha3)
-        self.assertEqual("840", transaction.billing_details.country_code_numeric)
-        self.assertEqual("Andrew", transaction.shipping_details.first_name)
-        self.assertEqual("Mason", transaction.shipping_details.last_name)
-        self.assertEqual("Braintree", transaction.shipping_details.company)
-        self.assertEqual("456 W Main St", transaction.shipping_details.street_address)
-        self.assertEqual("Apt 2F", transaction.shipping_details.extended_address)
-        self.assertEqual("Bartlett", transaction.shipping_details.locality)
-        self.assertEqual("IL", transaction.shipping_details.region)
-        self.assertEqual("60103", transaction.shipping_details.postal_code)
-        self.assertEqual("Mexico", transaction.shipping_details.country_name)
-        self.assertEqual("MX", transaction.shipping_details.country_code_alpha2)
-        self.assertEqual("MEX", transaction.shipping_details.country_code_alpha3)
-        self.assertEqual("484", transaction.shipping_details.country_code_numeric)
-        self.assertEqual(None, transaction.additional_processor_response)
-
-    def test_sale_with_vault_customer_and_credit_card_data(self):
-        customer = Customer.create({
-            "first_name": "Pingu",
-            "last_name": "Penguin",
-        }).customer
-
-        result = Transaction.sale({
-            "amount": Decimal(TransactionAmounts.Authorize),
-            "customer_id": customer.id,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-        self.assertEqual(transaction.credit_card_details.masked_number, "411111******1111")
-        self.assertEqual(None, transaction.vault_credit_card)
-
-    def test_sale_with_vault_customer_and_credit_card_data_and_store_in_vault(self):
-        customer = Customer.create({
-            "first_name": "Pingu",
-            "last_name": "Penguin",
-        }).customer
-
-        result = Transaction.sale({
-            "amount": Decimal(TransactionAmounts.Authorize),
-            "customer_id": customer.id,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            },
-            "options": {
-                "store_in_vault": True
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-        self.assertEqual("411111******1111", transaction.credit_card_details.masked_number)
-        self.assertEqual("411111******1111", transaction.vault_credit_card.masked_number)
-
-    def test_sale_with_venmo_merchant_data(self):
-        result = Transaction.sale({
-            "amount": Decimal(TransactionAmounts.Authorize),
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            },
-            "options": {
-                "venmo_merchant_data": {
-                    "venmo_merchant_public_id": "12345",
-                    "originating_transaction_id": "abc123",
-                    "originating_merchant_id": "xyz123",
-                    "originating_merchant_kind": "braintree",
-                }
-            }
-        })
-
-        self.assertTrue(result.is_success)
-
-    def test_sale_with_custom_fields(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            },
-            "custom_fields": {
-                "store_me": "some extra stuff"
-            }
-
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-        self.assertEqual("some extra stuff", transaction.custom_fields["store_me"])
-
-    def test_sale_with_merchant_account_id(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "merchant_account_id": TestHelper.non_default_merchant_account_id,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-        self.assertEqual(TestHelper.non_default_merchant_account_id, transaction.merchant_account_id)
-
-    def test_sale_without_merchant_account_id_falls_back_to_default(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-        self.assertEqual(TestHelper.default_merchant_account_id, transaction.merchant_account_id)
-
-    def test_sale_with_shipping_address_id(self):
-        result = Customer.create({
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2010"
-            }
-        })
-        self.assertTrue(result.is_success)
-        customer = result.customer
-
-        result = Address.create({
-            "customer_id": customer.id,
-            "street_address": "123 Fake St."
-        })
-        self.assertTrue(result.is_success)
-        address = result.address
-
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "customer_id": customer.id,
-            "shipping_address_id": address.id,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-        self.assertEqual("123 Fake St.", transaction.shipping_details.street_address)
-        self.assertEqual(address.id, transaction.shipping_details.id)
-
-    def test_sale_with_risk_data_security_parameters(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            },
-            "risk_data": {
-                "customer_browser": "IE7",
-                "customer_ip": "192.168.0.1"
-            }
-        })
-
-        self.assertTrue(result.is_success)
-
-    def test_sale_with_billing_address_id(self):
-        result = Customer.create({
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2010"
-            }
-        })
-        self.assertTrue(result.is_success)
-        customer = result.customer
-
-        result = Address.create({
-            "customer_id": customer.id,
-            "street_address": "123 Fake St."
-        })
-        self.assertTrue(result.is_success)
-        address = result.address
-
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "customer_id": customer.id,
-            "billing_address_id": address.id,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-        self.assertEqual("123 Fake St.", transaction.billing_details.street_address)
-        self.assertEqual(address.id, transaction.billing_details.id)
-
-    def test_sale_with_device_session_id_and_fraud_merchant_id(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2010"
-            },
-            "device_session_id": "abc123",
-            "fraud_merchant_id": "456"
-        })
-
-        self.assertTrue(result.is_success)
-
-
-    def test_sale_with_level_2(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "purchase_order_number": "12345",
-            "tax_amount": Decimal("10.00"),
-            "tax_exempt": True,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-        self.assertEqual("12345", transaction.purchase_order_number)
-        self.assertEqual(Decimal("10.00"), transaction.tax_amount)
-        self.assertEqual(True, transaction.tax_exempt)
-
-    def test_create_with_invalid_tax_amount(self):
-        result = Transaction.sale({
-            "amount": Decimal("100"),
-            "tax_amount": "asdf",
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            }
-        })
-        self.assertFalse(result.is_success)
-
-        tax_amount_errors = result.errors.for_object("transaction").on("tax_amount")
-        self.assertEqual(1, len(tax_amount_errors))
-        self.assertEqual(ErrorCodes.Transaction.TaxAmountFormatIsInvalid, tax_amount_errors[0].code)
-
-    def test_create_with_too_long_purchase_order_number(self):
-        result = Transaction.sale({
-            "amount": Decimal("100"),
-            "purchase_order_number": "aaaaaaaaaaaaaaaaaa",
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            }
-        })
-        self.assertFalse(result.is_success)
-
-        purchase_order_number_errors = result.errors.for_object("transaction").on("purchase_order_number")
-        self.assertEqual(1, len(purchase_order_number_errors))
-        self.assertEqual(ErrorCodes.Transaction.PurchaseOrderNumberIsTooLong, purchase_order_number_errors[0].code)
-
-    def test_create_with_invalid_purchase_order_number(self):
-        result = Transaction.sale({
-            "amount": Decimal("100"),
-            "purchase_order_number": "\xc3\x9f\xc3\xa5\xe2\x88\x82",
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            }
-        })
-        self.assertFalse(result.is_success)
-
-        purchase_order_number_errors = result.errors.for_object("transaction").on("purchase_order_number")
-        self.assertEqual(1, len(purchase_order_number_errors))
-        self.assertEqual(ErrorCodes.Transaction.PurchaseOrderNumberIsInvalid, purchase_order_number_errors[0].code)
-
-    def test_sale_with_level_3(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "purchase_order_number": "12345",
-            "discount_amount": Decimal("1.00"),
-            "shipping_amount": Decimal("2.00"),
-            "ships_from_postal_code": "12345",
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-        self.assertEqual(Decimal("1.00"), transaction.discount_amount)
-        self.assertEqual(Decimal("2.00"), transaction.shipping_amount)
-        self.assertEqual("12345", transaction.ships_from_postal_code)
-
-    def test_create_with_discount_amount_invalid(self):
-        result = Transaction.sale({
-            "amount": Decimal("100"),
-            "discount_amount": "asdf",
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            }
-        })
-        self.assertFalse(result.is_success)
-
-        errors = result.errors.for_object("transaction").on("discount_amount")
-        self.assertEqual(1, len(errors))
-        self.assertEqual(ErrorCodes.Transaction.DiscountAmountFormatIsInvalid, errors[0].code)
-
-    def test_create_with_discount_amount_negative(self):
-        result = Transaction.sale({
-            "amount": Decimal("100"),
-            "discount_amount": Decimal("-100"),
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            }
-        })
-        self.assertFalse(result.is_success)
-
-        errors = result.errors.for_object("transaction").on("discount_amount")
-        self.assertEqual(1, len(errors))
-        self.assertEqual(ErrorCodes.Transaction.DiscountAmountCannotBeNegative, errors[0].code)
-
-    def test_create_with_discount_amount_too_large(self):
-        result = Transaction.sale({
-            "amount": Decimal("100"),
-            "discount_amount": Decimal("999999999"),
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            }
-        })
-        self.assertFalse(result.is_success)
-
-        errors = result.errors.for_object("transaction").on("discount_amount")
-        self.assertEqual(1, len(errors))
-        self.assertEqual(ErrorCodes.Transaction.DiscountAmountIsTooLarge, errors[0].code)
-
-    def test_create_with_shipping_amount_invalid(self):
-        result = Transaction.sale({
-            "amount": Decimal("100"),
-            "shipping_amount": "asdf",
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            }
-        })
-        self.assertFalse(result.is_success)
-
-        errors = result.errors.for_object("transaction").on("shipping_amount")
-        self.assertEqual(1, len(errors))
-        self.assertEqual(ErrorCodes.Transaction.ShippingAmountFormatIsInvalid, errors[0].code)
-
-    def test_create_with_shipping_amount_negative(self):
-        result = Transaction.sale({
-            "amount": Decimal("100"),
-            "shipping_amount": Decimal("-100"),
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            }
-        })
-        self.assertFalse(result.is_success)
-
-        errors = result.errors.for_object("transaction").on("shipping_amount")
-        self.assertEqual(1, len(errors))
-        self.assertEqual(ErrorCodes.Transaction.ShippingAmountCannotBeNegative, errors[0].code)
-
-    def test_create_with_shipping_amount_too_large(self):
-        result = Transaction.sale({
-            "amount": Decimal("100"),
-            "shipping_amount": Decimal("999999999"),
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            }
-        })
-        self.assertFalse(result.is_success)
-
-        errors = result.errors.for_object("transaction").on("shipping_amount")
-        self.assertEqual(1, len(errors))
-        self.assertEqual(ErrorCodes.Transaction.ShippingAmountIsTooLarge, errors[0].code)
-
-    def test_create_with_ships_from_postal_code_is_too_long(self):
-        result = Transaction.sale({
-            "amount": Decimal("100"),
-            "ships_from_postal_code": "0000000000",
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            }
-        })
-        self.assertFalse(result.is_success)
-
-        errors = result.errors.for_object("transaction").on("ships_from_postal_code")
-        self.assertEqual(1, len(errors))
-        self.assertEqual(ErrorCodes.Transaction.ShipsFromPostalCodeIsTooLong, errors[0].code)
-
-    def test_create_with_ships_from_postal_code_invalid_characters(self):
-        result = Transaction.sale({
-            "amount": Decimal("100"),
-            "ships_from_postal_code": "1$345",
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            }
-        })
-        self.assertFalse(result.is_success)
-
-        errors = result.errors.for_object("transaction").on("ships_from_postal_code")
-        self.assertEqual(1, len(errors))
-        self.assertEqual(ErrorCodes.Transaction.ShipsFromPostalCodeInvalidCharacters, errors[0].code)
-
-    def test_sale_with_soft_declined(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Decline,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            }
-        })
-
-        self.assertFalse(result.is_success)
-        transaction = result.transaction
-        self.assertEqual(Transaction.Status.ProcessorDeclined, transaction.status)
-        self.assertEqual(ProcessorResponseTypes.SoftDeclined, transaction.processor_response_type)
-        self.assertEqual("2000 : Do Not Honor", transaction.additional_processor_response)
-
-    def test_sale_with_hard_declined(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.HardDecline,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            }
-        })
-
-        self.assertFalse(result.is_success)
-        transaction = result.transaction
-        self.assertEqual(Transaction.Status.ProcessorDeclined, transaction.status)
-        self.assertEqual(ProcessorResponseTypes.HardDeclined, transaction.processor_response_type)
-        self.assertEqual("2015 : Transaction Not Allowed", transaction.additional_processor_response)
-
-    def test_sale_with_gateway_rejected_with_incomplete_application(self):
-        gateway = BraintreeGateway(
-            client_id="client_id$development$integration_client_id",
-            client_secret="client_secret$development$integration_client_secret",
-            environment=Environment.Development
-        )
-
-        result = gateway.merchant.create({
-            "email": "name@email.com",
-            "country_code_alpha3": "USA",
-            "payment_methods": ["credit_card", "paypal"]
-        })
-
-        gateway = BraintreeGateway(
-            access_token=result.credentials.access_token,
-            environment=Environment.Development
-        )
-
-        result = gateway.transaction.sale({
-            "amount": "4000.00",
-            "billing": {
-                "street_address": "200 Fake Street"
-            },
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            }
-        })
-
-        self.assertFalse(result.is_success)
-        transaction = result.transaction
-        self.assertEqual(Transaction.GatewayRejectionReason.ApplicationIncomplete, transaction.gateway_rejection_reason)
-
-    def test_sale_with_gateway_rejected_with_avs(self):
-        old_merchant_id = Configuration.merchant_id
-        old_public_key = Configuration.public_key
-        old_private_key = Configuration.private_key
-
-        try:
-            Configuration.merchant_id = "processing_rules_merchant_id"
-            Configuration.public_key = "processing_rules_public_key"
-            Configuration.private_key = "processing_rules_private_key"
-
-            result = Transaction.sale({
-                "amount": TransactionAmounts.Authorize,
-                "billing": {
-                    "street_address": "200 Fake Street"
-                },
-                "credit_card": {
-                    "number": "4111111111111111",
-                    "expiration_date": "05/2009"
-                }
-            })
-
-            self.assertFalse(result.is_success)
-            transaction = result.transaction
-            self.assertEqual(Transaction.GatewayRejectionReason.Avs, transaction.gateway_rejection_reason)
-        finally:
-            Configuration.merchant_id = old_merchant_id
-            Configuration.public_key = old_public_key
-            Configuration.private_key = old_private_key
-
-    def test_sale_with_gateway_rejected_with_avs_and_cvv(self):
-        old_merchant_id = Configuration.merchant_id
-        old_public_key = Configuration.public_key
-        old_private_key = Configuration.private_key
-
-        try:
-            Configuration.merchant_id = "processing_rules_merchant_id"
-            Configuration.public_key = "processing_rules_public_key"
-            Configuration.private_key = "processing_rules_private_key"
-
-            result = Transaction.sale({
-                "amount": TransactionAmounts.Authorize,
-                "billing": {
-                    "postal_code": "20000"
-                },
-                "credit_card": {
-                    "number": "4111111111111111",
-                    "expiration_date": "05/2009",
-                    "cvv": "200"
-                }
-            })
-
-            self.assertFalse(result.is_success)
-            transaction = result.transaction
-            self.assertEqual(Transaction.GatewayRejectionReason.AvsAndCvv, transaction.gateway_rejection_reason)
-        finally:
-            Configuration.merchant_id = old_merchant_id
-            Configuration.public_key = old_public_key
-            Configuration.private_key = old_private_key
-
-    def test_sale_with_gateway_rejected_with_cvv(self):
-        old_merchant_id = Configuration.merchant_id
-        old_public_key = Configuration.public_key
-        old_private_key = Configuration.private_key
-
-        try:
-            Configuration.merchant_id = "processing_rules_merchant_id"
-            Configuration.public_key = "processing_rules_public_key"
-            Configuration.private_key = "processing_rules_private_key"
-
-            result = Transaction.sale({
-                "amount": TransactionAmounts.Authorize,
-                "credit_card": {
-                    "number": "4111111111111111",
-                    "expiration_date": "05/2009",
-                    "cvv": "200"
-                }
-            })
-
-            self.assertFalse(result.is_success)
-            transaction = result.transaction
-            self.assertEqual(Transaction.GatewayRejectionReason.Cvv, transaction.gateway_rejection_reason)
-        finally:
-            Configuration.merchant_id = old_merchant_id
-            Configuration.public_key = old_public_key
-            Configuration.private_key = old_private_key
-
-    def test_sale_with_gateway_rejected_with_fraud(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4000111111111511",
-                "expiration_date": "05/2017",
-                "cvv": "333"
-            }
-        })
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(Transaction.GatewayRejectionReason.Fraud, result.transaction.gateway_rejection_reason)
-
-    def test_sale_with_gateway_rejected_token_issuance(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "merchant_account_id": TestHelper.fake_venmo_account_merchant_account_id,
-            "payment_method_nonce": Nonces.VenmoAccountTokenIssuanceError
-        })
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(Transaction.GatewayRejectionReason.TokenIssuance, result.transaction.gateway_rejection_reason)
-
-    def test_sale_with_service_fee(self):
-        result = Transaction.sale({
-            "amount": "10.00",
-            "merchant_account_id": TestHelper.non_default_sub_merchant_account_id,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            },
-            "service_fee_amount": "1.00"
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-        self.assertEqual(transaction.service_fee_amount, "1.00")
-
-    def test_sale_on_master_merchant_accoount_is_invalid_with_service_fee(self):
-        result = Transaction.sale({
-            "amount": "10.00",
-            "merchant_account_id": TestHelper.non_default_merchant_account_id,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            },
-            "service_fee_amount": "1.00"
-        })
-        self.assertFalse(result.is_success)
-
-        amount_errors = result.errors.for_object("transaction").on("service_fee_amount")
-        self.assertEqual(1, len(amount_errors))
-        self.assertEqual(ErrorCodes.Transaction.ServiceFeeAmountNotAllowedOnMasterMerchantAccount, amount_errors[0].code)
-
-    def test_sale_on_submerchant_is_invalid_without_with_service_fee(self):
-        result = Transaction.sale({
-            "amount": "10.00",
-            "merchant_account_id": TestHelper.non_default_sub_merchant_account_id,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            }
-        })
-        self.assertFalse(result.is_success)
-        self.assertEqual(
-            ErrorCodes.Transaction.SubMerchantAccountRequiresServiceFeeAmount,
-            result.errors.for_object("transaction").on("merchant_account_id")[0].code
-        )
-
-    def test_sale_with_hold_in_escrow_option(self):
-        result = Transaction.sale({
-            "amount": "10.00",
-            "merchant_account_id": TestHelper.non_default_sub_merchant_account_id,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            },
-            "options": {
-                "hold_in_escrow": True
-            },
-            "service_fee_amount": "1.00"
-        })
-        self.assertTrue(result.is_success)
-        self.assertEqual(
-            Transaction.EscrowStatus.HoldPending,
-            result.transaction.escrow_status
-        )
-
-    def test_sale_with_hold_in_escrow_option_fails_for_master_merchant_account(self):
-        result = Transaction.sale({
-            "amount": "10.00",
-            "merchant_account_id": TestHelper.non_default_merchant_account_id,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            },
-            "options": {
-                "hold_in_escrow": True
-            }
-        })
-        self.assertFalse(result.is_success)
-        self.assertEqual(
-            ErrorCodes.Transaction.CannotHoldInEscrow,
-            result.errors.for_object("transaction").on("base")[0].code
-        )
-
-    def test_hold_in_escrow_after_sale(self):
-        result = Transaction.sale({
-            "amount": "10.00",
-            "merchant_account_id": TestHelper.non_default_sub_merchant_account_id,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            },
-            "service_fee_amount": "1.00"
-        })
-        self.assertTrue(result.is_success)
-        result = Transaction.hold_in_escrow(result.transaction.id)
-        self.assertTrue(result.is_success)
-        self.assertEqual(
-            Transaction.EscrowStatus.HoldPending,
-            result.transaction.escrow_status
-        )
-
-    def test_hold_in_escrow_after_sale_fails_for_master_merchant_account(self):
-        result = Transaction.sale({
-            "amount": "10.00",
-            "merchant_account_id": TestHelper.non_default_merchant_account_id,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            }
-        })
-        self.assertTrue(result.is_success)
-        result = Transaction.hold_in_escrow(result.transaction.id)
-        self.assertFalse(result.is_success)
-        self.assertEqual(
-            ErrorCodes.Transaction.CannotHoldInEscrow,
-            result.errors.for_object("transaction").on("base")[0].code
-        )
-
-    def test_release_from_escrow_from_escrow(self):
-        transaction = self.__create_escrowed_transaction()
-        result = Transaction.release_from_escrow(transaction.id)
-        self.assertTrue(result.is_success)
-        self.assertEqual(
-            Transaction.EscrowStatus.ReleasePending,
-            result.transaction.escrow_status
-        )
-
-
-    def test_release_from_escrow_from_escrow_fails_when_transaction_not_in_escrow(self):
-        result = Transaction.sale({
-            "amount": "10.00",
-            "merchant_account_id": TestHelper.non_default_merchant_account_id,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            }
-        })
-        self.assertTrue(result.is_success)
-        result = Transaction.release_from_escrow(result.transaction.id)
-        self.assertFalse(result.is_success)
-        self.assertEqual(
-            ErrorCodes.Transaction.CannotReleaseFromEscrow,
-            result.errors.for_object("transaction").on("base")[0].code
-        )
-
-    def test_cancel_release_from_escrow(self):
-        transaction = self.__create_escrowed_transaction()
-        submit_result = Transaction.release_from_escrow(transaction.id)
-        result = Transaction.cancel_release(submit_result.transaction.id)
-        self.assertTrue(result.is_success)
-        self.assertEqual(
-                Transaction.EscrowStatus.Held,
-                result.transaction.escrow_status
-        )
-
-    def test_cancel_release_from_escrow_fails_if_transaction_is_not_pending_release(self):
-        transaction = self.__create_escrowed_transaction()
-        result = Transaction.cancel_release(transaction.id)
-        self.assertFalse(result.is_success)
-        self.assertEqual(
-            ErrorCodes.Transaction.CannotCancelRelease,
-            result.errors.for_object("transaction").on("base")[0].code
-        )
-
-    def test_sale_with_venmo_sdk_session(self):
-        result = Transaction.sale({
-            "amount": "10.00",
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            },
-            "options": {
-                "venmo_sdk_session": venmo_sdk.Session
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertFalse(result.transaction.credit_card_details.venmo_sdk)
-
-    def test_sale_with_venmo_sdk_payment_method_code(self):
-        result = Transaction.sale({
-            "amount": "10.00",
-            "venmo_sdk_payment_method_code": venmo_sdk.VisaPaymentMethodCode
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-        self.assertEqual("411111", transaction.credit_card_details.bin)
-
-    def test_sale_with_payment_method_nonce(self):
-        config = Configuration.instantiate()
-        parsed_client_token = TestHelper.generate_decoded_client_token()
-        authorization_fingerprint = json.loads(parsed_client_token)["authorizationFingerprint"]
-        http = ClientApiHttp(config, {
-            "authorization_fingerprint": authorization_fingerprint,
-            "shared_customer_identifier": "fake_identifier",
-            "shared_customer_identifier_type": "testing"
-        })
-        _, response = http.add_card({
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_month": "11",
-                "expiration_year": "2099",
-            },
-            "share": True
-        })
-        nonce = json.loads(response)["creditCards"][0]["nonce"]
-
-
-        result = Transaction.sale({
-            "amount": "10.00",
-            "payment_method_nonce": nonce
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-        self.assertEqual("411111", transaction.credit_card_details.bin)
-
-    def test_sale_with_fake_apple_pay_nonce(self):
-        result = Transaction.sale({
-            "amount": "10.00",
-            "payment_method_nonce": Nonces.ApplePayAmEx
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertEqual(result.transaction.amount, 10.00)
-        self.assertEqual(result.transaction.payment_instrument_type, PaymentInstrumentType.ApplePayCard)
-        apple_pay_details = result.transaction.apple_pay_details
-        self.assertNotEqual(None, apple_pay_details)
-        self.assertEqual(ApplePayCard.CardType.AmEx, apple_pay_details.card_type)
-        self.assertEqual("AmEx 41002", apple_pay_details.payment_instrument_name)
-        self.assertTrue(int(apple_pay_details.expiration_month) > 0)
-        self.assertTrue(int(apple_pay_details.expiration_year) > 0)
-        self.assertNotEqual(None, apple_pay_details.cardholder_name)
-
-    def test_sale_with_fake_android_pay_proxy_card_nonce(self):
-        result = Transaction.sale({
-            "amount": "10.00",
-            "payment_method_nonce": Nonces.AndroidPayCardDiscover
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertEqual(result.transaction.amount, 10.00)
-        self.assertEqual(result.transaction.payment_instrument_type, PaymentInstrumentType.AndroidPayCard)
-        android_pay_card_details = result.transaction.android_pay_card_details
-        self.assertNotEqual(None, android_pay_card_details)
-        self.assertEqual(CreditCard.CardType.Discover, android_pay_card_details.card_type)
-        self.assertTrue(int(android_pay_card_details.expiration_month) > 0)
-        self.assertTrue(int(android_pay_card_details.expiration_year) > 0)
-
-    def test_sale_with_fake_android_pay_network_token_nonce(self):
-        result = Transaction.sale({
-            "amount": "10.00",
-            "payment_method_nonce": Nonces.AndroidPayCardMasterCard
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertEqual(result.transaction.amount, 10.00)
-        self.assertEqual(result.transaction.payment_instrument_type, PaymentInstrumentType.AndroidPayCard)
-        android_pay_card_details = result.transaction.android_pay_card_details
-        self.assertNotEqual(None, android_pay_card_details)
-        self.assertEqual(CreditCard.CardType.MasterCard, android_pay_card_details.card_type)
-        self.assertTrue(int(android_pay_card_details.expiration_month) > 0)
-        self.assertTrue(int(android_pay_card_details.expiration_year) > 0)
-
-    def test_sale_with_fake_amex_express_checkout_card_nonce(self):
-        result = Transaction.sale({
-            "amount": "10.00",
-            "payment_method_nonce": Nonces.AmexExpressCheckoutCard,
-            "merchant_account_id": TestHelper.fake_amex_direct_merchant_account_id,
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertEqual(result.transaction.amount, 10.00)
-        self.assertEqual(result.transaction.payment_instrument_type, PaymentInstrumentType.AmexExpressCheckoutCard)
-        amex_express_checkout_card_details = result.transaction.amex_express_checkout_card_details
-        self.assertNotEqual(None, amex_express_checkout_card_details)
-        self.assertEqual(CreditCard.CardType.AmEx, amex_express_checkout_card_details.card_type)
-        self.assertTrue(int(amex_express_checkout_card_details.expiration_month) > 0)
-        self.assertTrue(int(amex_express_checkout_card_details.expiration_year) > 0)
-
-    def test_sale_with_fake_venmo_account_nonce(self):
-        result = Transaction.sale({
-            "amount": "10.00",
-            "payment_method_nonce": Nonces.VenmoAccount,
-            "merchant_account_id": TestHelper.fake_venmo_account_merchant_account_id,
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertEqual(result.transaction.amount, 10.00)
-        self.assertEqual(result.transaction.payment_instrument_type, PaymentInstrumentType.VenmoAccount)
-
-        venmo_account_details = result.transaction.venmo_account_details
-        self.assertIsNotNone(venmo_account_details)
-        self.assertEqual(venmo_account_details.username, "venmojoe")
-        self.assertEqual(venmo_account_details.venmo_user_id, "Venmo-Joe-1")
-
-    def test_sale_with_fake_venmo_account_nonce_and_profile_id(self):
-        result = Transaction.sale({
-            "amount": "10.00",
-            "payment_method_nonce": Nonces.VenmoAccount,
-            "merchant_account_id": TestHelper.fake_venmo_account_merchant_account_id,
-            "options": {
-                "venmo": {
-                    "profile_id": "integration_venmo_merchant_public_id",
-                    },
-                },
-            })
-
-        self.assertTrue(result.is_success)
-
-    def test_sale_with_advanced_fraud_checking_skipped(self):
-        with AdvancedFraudIntegrationMerchant():
-            result = Transaction.sale({
-                "amount": TransactionAmounts.Authorize,
-                "credit_card": {
-                    "number": CreditCardNumbers.Visa,
-                    "expiration_date": "05/2009"
-                },
-                "options": {
-                    "skip_advanced_fraud_checking": True
-                }
-            })
-
-            self.assertTrue(result.is_success)
-            transaction = result.transaction
-            self.assertEqual(transaction.risk_data, None)
-
-    def test_sale_with_skip_cvv_option_set(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": CreditCardNumbers.Visa,
-                "expiration_date": "05/2009"
-            },
-            "options": {
-                "skip_cvv": True
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-        self.assertEqual(transaction.cvv_response_code, "B")
-
-    def test_sale_with_skip_avs_option_set(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": CreditCardNumbers.Visa,
-                "expiration_date": "05/2009"
-            },
-            "options": {
-                "skip_avs": True
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-        self.assertEqual(transaction.avs_error_response_code, None)
-        self.assertEqual(transaction.avs_street_address_response_code, "B")
-
-    def test_sale_with_line_items_zero(self):
-        result = Transaction.sale({
-            "amount": "45.15",
-            "credit_card": {
-                "number": CreditCardNumbers.Visa,
-                "expiration_date": "05/2009",
-            }
-        })
-
-        self.assertTrue(result.is_success)
-
-        transaction = result.transaction
-
-        line_items = transaction.line_items
-        self.assertEqual(0, len(line_items))
-
-    def test_sale_with_line_items_single_only_required_fields(self):
-        result = Transaction.sale({
-            "amount": "35.05",
-            "credit_card": {
-                "number": CreditCardNumbers.Visa,
-                "expiration_date": "05/2009",
-            },
-            "line_items": [{
-                "quantity": "1.0232",
-                "name": "Name #1",
-                "kind": TransactionLineItem.Kind.Debit,
-                "unit_amount": "45.1232",
-                "total_amount": "45.15",
-            }]
-        })
-
-        self.assertTrue(result.is_success)
-
-        transaction = result.transaction
-
-        line_items = transaction.line_items
-        self.assertEqual(1, len(line_items))
-
-        lineItem = line_items[0]
-        self.assertEqual("1.0232", lineItem.quantity)
-        self.assertEqual("Name #1", lineItem.name)
-        self.assertEqual(TransactionLineItem.Kind.Debit, lineItem.kind)
-        self.assertEqual("45.1232", lineItem.unit_amount)
-        self.assertEqual("45.15", lineItem.total_amount)
-
-    def test_sale_with_line_items_single(self):
-        result = Transaction.sale({
-            "amount": "45.15",
-            "credit_card": {
-                "number": CreditCardNumbers.Visa,
-                "expiration_date": "05/2009",
-            },
-            "line_items": [{
-                "quantity": "1.0232",
-                "name": "Name #1",
-                "description": "Description #1",
-                "kind": TransactionLineItem.Kind.Debit,
-                "unit_amount": "45.1232",
-                "unit_tax_amount": "1.23",
-                "unit_of_measure": "gallon",
-                "discount_amount": "1.02",
-                "tax_amount": "4.50",
-                "total_amount": "45.15",
-                "product_code": "23434",
-                "commodity_code": "9SAASSD8724",
-                "url": "https://example.com/products/23434",
-            }]
-        })
-
-        self.assertTrue(result.is_success)
-
-        transaction = result.transaction
-
-        line_items = transaction.line_items
-        self.assertEqual(1, len(line_items))
-
-        lineItem = line_items[0]
-        self.assertEqual("1.0232", lineItem.quantity)
-        self.assertEqual("Name #1", lineItem.name)
-        self.assertEqual("Description #1", lineItem.description)
-        self.assertEqual(TransactionLineItem.Kind.Debit, lineItem.kind)
-        self.assertEqual("45.1232", lineItem.unit_amount)
-        self.assertEqual("1.23", lineItem.unit_tax_amount)
-        self.assertEqual("gallon", lineItem.unit_of_measure)
-        self.assertEqual("1.02", lineItem.discount_amount)
-        self.assertEqual("4.50", lineItem.tax_amount)
-        self.assertEqual("45.15", lineItem.total_amount)
-        self.assertEqual("23434", lineItem.product_code)
-        self.assertEqual("9SAASSD8724", lineItem.commodity_code)
-        self.assertEqual("https://example.com/products/23434", lineItem.url)
-
-    def test_sale_with_line_items_multiple(self):
-        result = Transaction.sale({
-            "amount": "35.05",
-            "credit_card": {
-                "number": CreditCardNumbers.Visa,
-                "expiration_date": "05/2009",
-            },
-            "line_items": [{
-                "quantity": "1.0232",
-                "name": "Name #1",
-                "description": "Description #1",
-                "kind": TransactionLineItem.Kind.Debit,
-                "unit_amount": "45.1232",
-                "unit_of_measure": "gallon",
-                "discount_amount": "1.02",
-                "tax_amount": "4.50",
-                "total_amount": "45.15",
-                "product_code": "23434",
-                "commodity_code": "9SAASSD8724",
-            },
-            {
-                "quantity": "2.02",
-                "name": "Name #2",
-                "description": "Description #2",
-                "kind": TransactionLineItem.Kind.Credit,
-                "unit_amount": "5",
-                "unit_of_measure": "gallon",
-                "tax_amount": "4.50",
-                "total_amount": "45.15",
-            }]
-        })
-
-        self.assertTrue(result.is_success)
-
-        transaction = result.transaction
-
-        line_items = transaction.line_items
-        self.assertEqual(2, len(line_items))
-
-        line_item_1 = None
-        for line_item in line_items:
-            if line_item.name == "Name #1":
-                line_item_1 = line_item
-                break
-        if line_item_1 is None:
-            self.fail("TransactionLineItem with name \"Name #1\" not returned.")
-        self.assertEqual("1.0232", line_item_1.quantity)
-        self.assertEqual("Name #1", line_item_1.name)
-        self.assertEqual("Description #1", line_item_1.description)
-        self.assertEqual(TransactionLineItem.Kind.Debit, line_item_1.kind)
-        self.assertEqual("45.1232", line_item_1.unit_amount)
-        self.assertEqual("gallon", line_item_1.unit_of_measure)
-        self.assertEqual("1.02", line_item_1.discount_amount)
-        self.assertEqual("45.15", line_item_1.total_amount)
-        self.assertEqual("23434", line_item_1.product_code)
-        self.assertEqual("9SAASSD8724", line_item_1.commodity_code)
-
-        line_item_2 = None
-        for line_item in line_items:
-            if line_item.name == "Name #2":
-                line_item_2 = line_item
-                break
-        if line_item_2 is None:
-            self.fail("TransactionLineItem with name \"Name #2\" not returned.")
-        self.assertEqual("2.02", line_item_2.quantity)
-        self.assertEqual("Name #2", line_item_2.name)
-        self.assertEqual("Description #2", line_item_2.description)
-        self.assertEqual(TransactionLineItem.Kind.Credit, line_item_2.kind)
-        self.assertEqual("5", line_item_2.unit_amount)
-        self.assertEqual("gallon", line_item_2.unit_of_measure)
-        self.assertEqual("45.15", line_item_2.total_amount)
-        self.assertEqual(None, line_item_2.discount_amount)
-        self.assertEqual(None, line_item_2.product_code)
-        self.assertEqual(None, line_item_2.commodity_code)
-
-    def test_sale_with_line_items_with_zero_amount_fields(self):
-        result = Transaction.sale({
-            "amount": "45.15",
-            "credit_card": {
-                "number": CreditCardNumbers.Visa,
-                "expiration_date": "05/2009",
-            },
-            "line_items": [{
-                "quantity": "1.0232",
-                "name": "Name #1",
-                "kind": TransactionLineItem.Kind.Debit,
-                "unit_amount": "45.1232",
-                "total_amount": "45.15",
-                "discount_amount": "0",
-                "unit_tax_amount": "0",
-                "tax_amount": "0",
-            }]
-        })
-
-        self.assertTrue(result.is_success)
-
-        transaction = result.transaction
-
-        line_items = transaction.line_items
-
-        lineItem = line_items[0]
-        self.assertEqual("0.00", lineItem.unit_tax_amount)
-        self.assertEqual("0.00", lineItem.discount_amount)
-        self.assertEqual("0.00", lineItem.tax_amount)
-
-    def test_sale_with_line_items_validation_error_commodity_code_is_too_long(self):
-        result = Transaction.sale({
-            "amount": "35.05",
-            "credit_card": {
-                "number": CreditCardNumbers.Visa,
-                "expiration_date": "05/2009",
-            },
-            "line_items": [{
-                "quantity": "1.0232",
-                "name": "Name #1",
-                "kind": TransactionLineItem.Kind.Debit,
-                "unit_amount": "45.1232",
-                "unit_of_measure": "gallon",
-                "discount_amount": "1.02",
-                "total_amount": "45.15",
-                "product_code": "23434",
-                "commodity_code": "9SAASSD8724",
-            },
-            {
-                "quantity": "1.0232",
-                "name": "Name #2",
-                "kind": TransactionLineItem.Kind.Debit,
-                "unit_amount": "45.1232",
-                "unit_of_measure": "gallon",
-                "discount_amount": "1.02",
-                "total_amount": "45.15",
-                "product_code": "23434",
-                "commodity_code": "0123456789123",
-            }]
-        })
-
-        self.assertFalse(result.is_success)
-
-        self.assertEqual(
-            ErrorCodes.Transaction.LineItem.CommodityCodeIsTooLong,
-            result.errors.for_object("transaction").for_object("line_items").for_object("index_1").on("commodity_code")[0].code
-        )
-
-    def test_sale_with_line_items_validation_error_description_is_too_long(self):
-        result = Transaction.sale({
-            "amount": "35.05",
-            "credit_card": {
-                "number": CreditCardNumbers.Visa,
-                "expiration_date": "05/2009",
-            },
-            "line_items": [{
-                "quantity": "1.0232",
-                "name": "Name #1",
-                "kind": TransactionLineItem.Kind.Debit,
-                "unit_amount": "45.1232",
-                "unit_of_measure": "gallon",
-                "discount_amount": "1.02",
-                "total_amount": "45.15",
-                "product_code": "23434",
-                "commodity_code": "9SAASSD8724",
-            },
-            {
-                "quantity": "1.0232",
-                "name": "Name #2",
-                "description": "This is a line item description which is far too long. Like, way too long to be practical. We don't like how long this line item description is.",
-                "kind": TransactionLineItem.Kind.Debit,
-                "unit_amount": "45.1232",
-                "unit_of_measure": "gallon",
-                "discount_amount": "1.02",
-                "total_amount": "45.15",
-                "product_code": "23434",
-                "commodity_code": "9SAASSD8724",
-            }]
-        })
-
-        self.assertFalse(result.is_success)
-
-        self.assertEqual(
-            ErrorCodes.Transaction.LineItem.DescriptionIsTooLong,
-            result.errors.for_object("transaction").for_object("line_items").for_object("index_1").on("description")[0].code
-        )
-
-    def test_sale_with_line_items_validation_error_discount_amount_is_too_large(self):
-        result = Transaction.sale({
-            "amount": "35.05",
-            "credit_card": {
-                "number": CreditCardNumbers.Visa,
-                "expiration_date": "05/2009",
-            },
-            "line_items": [{
-                "quantity": "1.0232",
-                "name": "Name #1",
-                "kind": TransactionLineItem.Kind.Debit,
-                "unit_amount": "45.1232",
-                "unit_of_measure": "gallon",
-                "discount_amount": "1.02",
-                "total_amount": "45.15",
-                "product_code": "23434",
-                "commodity_code": "9SAASSD8724",
-            },
-            {
-                "quantity": "1.0232",
-                "name": "Name #2",
-                "kind": TransactionLineItem.Kind.Debit,
-                "unit_amount": "45.1232",
-                "unit_of_measure": "gallon",
-                "discount_amount": "2147483648",
-                "total_amount": "45.15",
-                "product_code": "23434",
-                "commodity_code": "9SAASSD8724",
-            }]
-        })
-
-        self.assertFalse(result.is_success)
-
-        self.assertEqual(
-            ErrorCodes.Transaction.LineItem.DiscountAmountIsTooLarge,
-            result.errors.for_object("transaction").for_object("line_items").for_object("index_1").on("discount_amount")[0].code
-        )
-
-    def test_sale_with_line_items_validation_error_discount_amount_cannot_be_negative(self):
-        result = Transaction.sale({
-            "amount": "35.05",
-            "credit_card": {
-                "number": CreditCardNumbers.Visa,
-                "expiration_date": "05/2009",
-            },
-            "line_items": [{
-                "quantity": "1.0232",
-                "name": "Name #1",
-                "kind": TransactionLineItem.Kind.Debit,
-                "unit_amount": "45.1232",
-                "unit_of_measure": "gallon",
-                "discount_amount": "1.02",
-                "total_amount": "45.15",
-                "product_code": "23434",
-                "commodity_code": "9SAASSD8724",
-            },
-            {
-                "quantity": "1.0232",
-                "name": "Name #2",
-                "kind": TransactionLineItem.Kind.Debit,
-                "unit_amount": "45.1232",
-                "unit_of_measure": "gallon",
-                "discount_amount": "-1.23",
-                "total_amount": "45.15",
-                "product_code": "23434",
-                "commodity_code": "9SAASSD8724",
-            }]
-        })
-
-        self.assertFalse(result.is_success)
-
-        self.assertEqual(
-            ErrorCodes.Transaction.LineItem.DiscountAmountCannotBeNegative,
-            result.errors.for_object("transaction").for_object("line_items").for_object("index_1").on("discount_amount")[0].code
-        )
-
-    def test_sale_with_line_items_validation_error_tax_amount_is_too_large(self):
-        result = Transaction.sale({
-            "amount": "35.05",
-            "credit_card": {
-                "number": CreditCardNumbers.Visa,
-                "expiration_date": "05/2009",
-            },
-            "line_items": [{
-                "quantity": "1.0232",
-                "name": "Name #2",
-                "kind": TransactionLineItem.Kind.Debit,
-                "unit_amount": "45.1232",
-                "unit_of_measure": "gallon",
-                "discount_amount": "1.02",
-                "tax_amount": "2147483648",
-                "total_amount": "45.15",
-                "product_code": "23434",
-                "commodity_code": "9SAASSD8724",
-            }]
-        })
-
-        self.assertFalse(result.is_success)
-
-        self.assertEqual(
-            ErrorCodes.Transaction.LineItem.TaxAmountIsTooLarge,
-            result.errors.for_object("transaction").for_object("line_items").for_object("index_0").on("tax_amount")[0].code
-        )
-
-    def test_sale_with_line_items_validation_error_tax_amount_cannot_be_negative(self):
-        result = Transaction.sale({
-            "amount": "35.05",
-            "credit_card": {
-                "number": CreditCardNumbers.Visa,
-                "expiration_date": "05/2009",
-            },
-            "line_items": [{
-                "quantity": "1.0232",
-                "name": "Name #1",
-                "kind": TransactionLineItem.Kind.Debit,
-                "unit_amount": "45.1232",
-                "unit_of_measure": "gallon",
-                "discount_amount": "1.02",
-                "tax_amount": "-1.23",
-                "total_amount": "45.15",
-                "product_code": "23434",
-                "commodity_code": "9SAASSD8724",
-            }]
-        })
-
-        self.assertFalse(result.is_success)
-
-        self.assertEqual(
-            ErrorCodes.Transaction.LineItem.TaxAmountCannotBeNegative,
-            result.errors.for_object("transaction").for_object("line_items").for_object("index_0").on("tax_amount")[0].code
-        )
-
-    def test_sale_with_line_items_validation_error_tax_amount_format_is_invalid(self):
-        result = Transaction.sale({
-            "amount": "35.05",
-            "credit_card": {
-                "number": CreditCardNumbers.Visa,
-                "expiration_date": "05/2009",
-            },
-            "line_items": [{
-                "quantity": "1.0232",
-                "name": "Name #1",
-                "kind": TransactionLineItem.Kind.Debit,
-                "unit_amount": "45.1232",
-                "unit_of_measure": "gallon",
-                "discount_amount": "1.02",
-                "tax_amount": "4.555",
-                "total_amount": "45.15",
-                "product_code": "23434",
-                "commodity_code": "9SAASSD8724",
-            }]
-        })
-
-        self.assertFalse(result.is_success)
-
-        self.assertEqual(
-            ErrorCodes.Transaction.LineItem.TaxAmountFormatIsInvalid,
-            result.errors.for_object("transaction").for_object("line_items").for_object("index_0").on("tax_amount")[0].code
-        )
-
-    def test_sale_with_line_items_validation_error_kind_is_required(self):
-        result = Transaction.sale({
-            "amount": "35.05",
-            "credit_card": {
-                "number": CreditCardNumbers.Visa,
-                "expiration_date": "05/2009",
-            },
-            "line_items": [{
-                "quantity": "1.0232",
-                "name": "Name #1",
-                "kind": TransactionLineItem.Kind.Debit,
-                "unit_amount": "45.1232",
-                "unit_of_measure": "gallon",
-                "discount_amount": "1.02",
-                "total_amount": "45.15",
-                "product_code": "23434",
-                "commodity_code": "9SAASSD8724",
-            },
-            {
-                "quantity": "1.0232",
-                "name": "Name #2",
-                "unit_amount": "45.1232",
-                "unit_of_measure": "gallon",
-                "discount_amount": "1.02",
-                "total_amount": "45.15",
-                "product_code": "23434",
-                "commodity_code": "9SAASSD8724",
-            }]
-        })
-
-        self.assertFalse(result.is_success)
-
-        self.assertEqual(
-            ErrorCodes.Transaction.LineItem.KindIsRequired,
-            result.errors.for_object("transaction").for_object("line_items").for_object("index_1").on("kind")[0].code
-        )
-
-    def test_sale_with_line_items_validation_error_name_is_required(self):
-        result = Transaction.sale({
-            "amount": "35.05",
-            "credit_card": {
-                "number": CreditCardNumbers.Visa,
-                "expiration_date": "05/2009",
-            },
-            "line_items": [{
-                "quantity": "1.0232",
-                "name": "Name #1",
-                "kind": TransactionLineItem.Kind.Debit,
-                "unit_amount": "45.1232",
-                "unit_of_measure": "gallon",
-                "discount_amount": "1.02",
-                "total_amount": "45.15",
-                "product_code": "23434",
-                "commodity_code": "9SAASSD8724",
-            },
-            {
-                "quantity": "1.0232",
-                "kind": TransactionLineItem.Kind.Debit,
-                "unit_amount": "45.1232",
-                "unit_of_measure": "gallon",
-                "discount_amount": "1.02",
-                "total_amount": "45.15",
-                "product_code": "23434",
-                "commodity_code": "9SAASSD8724",
-            }]
-        })
-
-        self.assertFalse(result.is_success)
-
-        self.assertEqual(
-            ErrorCodes.Transaction.LineItem.NameIsRequired,
-            result.errors.for_object("transaction").for_object("line_items").for_object("index_1").on("name")[0].code
-        )
-
-    def test_sale_with_line_items_validation_error_name_is_too_long(self):
-        result = Transaction.sale({
-            "amount": "35.05",
-            "credit_card": {
-                "number": CreditCardNumbers.Visa,
-                "expiration_date": "05/2009",
-            },
-            "line_items": [{
-                "quantity": "1.0232",
-                "name": "Name #1",
-                "kind": TransactionLineItem.Kind.Debit,
-                "unit_amount": "45.1232",
-                "unit_of_measure": "gallon",
-                "discount_amount": "1.02",
-                "total_amount": "45.15",
-                "product_code": "23434",
-                "commodity_code": "9SAASSD8724",
-            },
-            {
-                "quantity": "1.0232",
-                "name": "123456789012345678901234567890123456",
-                "kind": TransactionLineItem.Kind.Debit,
-                "unit_amount": "45.1232",
-                "unit_of_measure": "gallon",
-                "discount_amount": "1.02",
-                "total_amount": "45.15",
-                "product_code": "23434",
-                "commodity_code": "9SAASSD8724",
-            }]
-        })
-
-        self.assertFalse(result.is_success)
-
-        self.assertEqual(
-            ErrorCodes.Transaction.LineItem.NameIsTooLong,
-            result.errors.for_object("transaction").for_object("line_items").for_object("index_1").on("name")[0].code
-        )
-
-    def test_sale_with_line_items_validation_error_product_code_is_too_long(self):
-        result = Transaction.sale({
-            "amount": "35.05",
-            "credit_card": {
-                "number": CreditCardNumbers.Visa,
-                "expiration_date": "05/2009",
-            },
-            "line_items": [{
-                "quantity": "1.0232",
-                "name": "Name #1",
-                "kind": TransactionLineItem.Kind.Debit,
-                "unit_amount": "45.1232",
-                "unit_of_measure": "gallon",
-                "discount_amount": "1.02",
-                "total_amount": "45.15",
-                "product_code": "23434",
-                "commodity_code": "9SAASSD8724",
-            },
-            {
-                "quantity": "1.0232",
-                "name": "Name #2",
-                "kind": TransactionLineItem.Kind.Credit,
-                "unit_amount": "45.1232",
-                "unit_of_measure": "gallon",
-                "discount_amount": "1.02",
-                "total_amount": "45.15",
-                "product_code": "123456789012345678901234567890123456",
-                "commodity_code": "9SAASSD8724",
-            }]
-        })
-
-        self.assertFalse(result.is_success)
-
-        self.assertEqual(
-            ErrorCodes.Transaction.LineItem.ProductCodeIsTooLong,
-            result.errors.for_object("transaction").for_object("line_items").for_object("index_1").on("product_code")[0].code
-        )
-
-    def test_sale_with_line_items_validation_error_quantity_is_required(self):
-        result = Transaction.sale({
-            "amount": "35.05",
-            "credit_card": {
-                "number": CreditCardNumbers.Visa,
-                "expiration_date": "05/2009",
-            },
-            "line_items": [{
-                "quantity": "1.0232",
-                "name": "Name #1",
-                "kind": TransactionLineItem.Kind.Debit,
-                "unit_amount": "45.1232",
-                "unit_of_measure": "gallon",
-                "discount_amount": "1.02",
-                "total_amount": "45.15",
-                "product_code": "23434",
-                "commodity_code": "9SAASSD8724",
-            },
-            {
-                "name": "Name #2",
-                "kind": TransactionLineItem.Kind.Credit,
-                "unit_amount": "45.1232",
-                "unit_of_measure": "gallon",
-                "discount_amount": "1.02",
-                "total_amount": "45.15",
-                "product_code": "23434",
-                "commodity_code": "9SAASSD8724",
-            }]
-        })
-
-        self.assertFalse(result.is_success)
-
-        self.assertEqual(
-            ErrorCodes.Transaction.LineItem.QuantityIsRequired,
-            result.errors.for_object("transaction").for_object("line_items").for_object("index_1").on("quantity")[0].code
-        )
-
-    def test_sale_with_line_items_validation_error_quantity_is_too_large(self):
-        result = Transaction.sale({
-            "amount": "35.05",
-            "credit_card": {
-                "number": CreditCardNumbers.Visa,
-                "expiration_date": "05/2009",
-            },
-            "line_items": [{
-                "quantity": "1.0232",
-                "name": "Name #1",
-                "kind": TransactionLineItem.Kind.Debit,
-                "unit_amount": "45.1232",
-                "unit_of_measure": "gallon",
-                "discount_amount": "1.02",
-                "total_amount": "45.15",
-                "product_code": "23434",
-                "commodity_code": "9SAASSD8724",
-            },
-            {
-                "quantity": "2147483648",
-                "name": "Name #2",
-                "kind": TransactionLineItem.Kind.Credit,
-                "unit_amount": "45.1232",
-                "unit_of_measure": "gallon",
-                "discount_amount": "1.02",
-                "total_amount": "45.15",
-                "product_code": "23434",
-                "commodity_code": "9SAASSD8724",
-            }]
-        })
-
-        self.assertFalse(result.is_success)
-
-        self.assertEqual(
-            ErrorCodes.Transaction.LineItem.QuantityIsTooLarge,
-            result.errors.for_object("transaction").for_object("line_items").for_object("index_1").on("quantity")[0].code
-        )
-
-    def test_sale_with_line_items_validation_error_total_amount_is_required(self):
-        result = Transaction.sale({
-            "amount": "35.05",
-            "credit_card": {
-                "number": CreditCardNumbers.Visa,
-                "expiration_date": "05/2009",
-            },
-            "line_items": [{
-                "quantity": "1.0232",
-                "name": "Name #1",
-                "kind": TransactionLineItem.Kind.Debit,
-                "unit_amount": "45.1232",
-                "unit_of_measure": "gallon",
-                "discount_amount": "1.02",
-                "total_amount": "45.15",
-                "product_code": "23434",
-                "commodity_code": "9SAASSD8724",
-            },
-            {
-                "quantity": "1.0232",
-                "name": "Name #2",
-                "kind": TransactionLineItem.Kind.Credit,
-                "unit_amount": "45.1232",
-                "unit_of_measure": "gallon",
-                "discount_amount": "1.02",
-                "product_code": "23434",
-                "commodity_code": "9SAASSD8724",
-            }]
-        })
-
-        self.assertFalse(result.is_success)
-
-        self.assertEqual(
-            ErrorCodes.Transaction.LineItem.TotalAmountIsRequired,
-            result.errors.for_object("transaction").for_object("line_items").for_object("index_1").on("total_amount")[0].code
-        )
-
-    def test_sale_with_line_items_validation_error_total_amount_is_too_large(self):
-        result = Transaction.sale({
-            "amount": "35.05",
-            "credit_card": {
-                "number": CreditCardNumbers.Visa,
-                "expiration_date": "05/2009",
-            },
-            "line_items": [{
-                "quantity": "1.0232",
-                "name": "Name #1",
-                "kind": TransactionLineItem.Kind.Debit,
-                "unit_amount": "45.1232",
-                "unit_of_measure": "gallon",
-                "discount_amount": "1.02",
-                "total_amount": "45.15",
-                "product_code": "23434",
-                "commodity_code": "9SAASSD8724",
-            },
-            {
-                "quantity": "1.0232",
-                "name": "Name #2",
-                "kind": TransactionLineItem.Kind.Credit,
-                "unit_amount": "45.1232",
-                "unit_of_measure": "gallon",
-                "discount_amount": "1.02",
-                "total_amount": "2147483648",
-                "product_code": "23434",
-                "commodity_code": "9SAASSD8724",
-            }]
-        })
-
-        self.assertFalse(result.is_success)
-
-        self.assertEqual(
-            ErrorCodes.Transaction.LineItem.TotalAmountIsTooLarge,
-            result.errors.for_object("transaction").for_object("line_items").for_object("index_1").on("total_amount")[0].code
-        )
-
-    def test_sale_with_line_items_validation_error_total_amount_must_be_greater_than_zero(self):
-        result = Transaction.sale({
-            "amount": "35.05",
-            "credit_card": {
-                "number": CreditCardNumbers.Visa,
-                "expiration_date": "05/2009",
-            },
-            "line_items": [{
-                "quantity": "1.0232",
-                "name": "Name #1",
-                "kind": TransactionLineItem.Kind.Debit,
-                "unit_amount": "45.1232",
-                "unit_of_measure": "gallon",
-                "discount_amount": "1.02",
-                "total_amount": "45.15",
-                "product_code": "23434",
-                "commodity_code": "9SAASSD8724",
-            },
-            {
-                "quantity": "1.0232",
-                "name": "Name #2",
-                "kind": TransactionLineItem.Kind.Credit,
-                "unit_amount": "45.1232",
-                "unit_of_measure": "gallon",
-                "discount_amount": "1.02",
-                "total_amount": "-2",
-                "product_code": "23434",
-                "commodity_code": "9SAASSD8724",
-            }]
-        })
-
-        self.assertFalse(result.is_success)
-
-        self.assertEqual(
-            ErrorCodes.Transaction.LineItem.TotalAmountMustBeGreaterThanZero,
-            result.errors.for_object("transaction").for_object("line_items").for_object("index_1").on("total_amount")[0].code
-        )
-
-    def test_sale_with_line_items_validation_error_unit_amount_is_required(self):
-        result = Transaction.sale({
-            "amount": "35.05",
-            "credit_card": {
-                "number": CreditCardNumbers.Visa,
-                "expiration_date": "05/2009",
-            },
-            "line_items": [{
-                "quantity": "1.0232",
-                "name": "Name #1",
-                "kind": TransactionLineItem.Kind.Debit,
-                "unit_amount": "45.1232",
-                "unit_of_measure": "gallon",
-                "discount_amount": "1.02",
-                "total_amount": "45.15",
-                "product_code": "23434",
-                "commodity_code": "9SAASSD8724",
-            },
-            {
-                "quantity": "1.0232",
-                "name": "Name #2",
-                "kind": TransactionLineItem.Kind.Credit,
-                "unit_of_measure": "gallon",
-                "discount_amount": "1.02",
-                "total_amount": "45.15",
-                "product_code": "23434",
-                "commodity_code": "9SAASSD8724",
-            }]
-        })
-
-        self.assertFalse(result.is_success)
-
-        self.assertEqual(
-            ErrorCodes.Transaction.LineItem.UnitAmountIsRequired,
-            result.errors.for_object("transaction").for_object("line_items").for_object("index_1").on("unit_amount")[0].code
-        )
-
-    def test_sale_with_line_items_validation_error_unit_amount_is_too_large(self):
-        result = Transaction.sale({
-            "amount": "35.05",
-            "credit_card": {
-                "number": CreditCardNumbers.Visa,
-                "expiration_date": "05/2009",
-            },
-            "line_items": [{
-                "quantity": "1.0232",
-                "name": "Name #1",
-                "kind": TransactionLineItem.Kind.Debit,
-                "unit_amount": "45.1232",
-                "unit_of_measure": "gallon",
-                "discount_amount": "1.02",
-                "total_amount": "45.15",
-                "product_code": "23434",
-                "commodity_code": "9SAASSD8724",
-            },
-            {
-                "quantity": "1.0232",
-                "name": "Name #2",
-                "kind": TransactionLineItem.Kind.Credit,
-                "unit_amount": "2147483648",
-                "unit_of_measure": "gallon",
-                "discount_amount": "1.02",
-                "total_amount": "45.15",
-                "product_code": "23434",
-                "commodity_code": "9SAASSD8724",
-            }]
-        })
-
-        self.assertFalse(result.is_success)
-
-        self.assertEqual(
-            ErrorCodes.Transaction.LineItem.UnitAmountIsTooLarge,
-            result.errors.for_object("transaction").for_object("line_items").for_object("index_1").on("unit_amount")[0].code
-        )
-
-    def test_sale_with_line_items_validation_error_unit_amount_must_be_greater_than_zero(self):
-        result = Transaction.sale({
-            "amount": "35.05",
-            "credit_card": {
-                "number": CreditCardNumbers.Visa,
-                "expiration_date": "05/2009",
-            },
-            "line_items": [{
-                "quantity": "1.0232",
-                "name": "Name #1",
-                "kind": TransactionLineItem.Kind.Debit,
-                "unit_amount": "45.1232",
-                "unit_of_measure": "gallon",
-                "discount_amount": "1.02",
-                "total_amount": "45.15",
-                "product_code": "23434",
-                "commodity_code": "9SAASSD8724",
-            },
-            {
-                "quantity": "1.0232",
-                "name": "Name #2",
-                "kind": TransactionLineItem.Kind.Credit,
-                "unit_amount": "-2",
-                "unit_of_measure": "gallon",
-                "discount_amount": "1.02",
-                "total_amount": "45.15",
-                "product_code": "23434",
-                "commodity_code": "9SAASSD8724",
-            }]
-        })
-
-        self.assertFalse(result.is_success)
-
-        self.assertEqual(
-            ErrorCodes.Transaction.LineItem.UnitAmountMustBeGreaterThanZero,
-            result.errors.for_object("transaction").for_object("line_items").for_object("index_1").on("unit_amount")[0].code
-        )
-
-    def test_sale_with_amount_not_supported_by_processor(self):
-        result = Transaction.sale({
-            "amount": "0.2",
-            "merchant_account_id": TestHelper.hiper_brl_merchant_account_id,
-            "credit_card": {
-                "number": CreditCardNumbers.Hiper,
-                "expiration_date": "10/2020",
-                "cvv": "737",
-            }
-        })
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(
-            ErrorCodes.Transaction.AmountNotSupportedByProcessor,
-            result.errors.for_object("transaction")[0].code
-        )
-
-    def test_sale_with_line_items_validation_error_unit_of_measure_is_too_large(self):
-        result = Transaction.sale({
-            "amount": "35.05",
-            "credit_card": {
-                "number": CreditCardNumbers.Visa,
-                "expiration_date": "05/2009",
-            },
-            "line_items": [{
-                "quantity": "1.0232",
-                "name": "Name #1",
-                "kind": TransactionLineItem.Kind.Debit,
-                "unit_amount": "45.1232",
-                "unit_of_measure": "gallon",
-                "discount_amount": "1.02",
-                "total_amount": "45.15",
-                "product_code": "23434",
-                "commodity_code": "9SAASSD8724",
-            },
-            {
-                "quantity": "1.0232",
-                "name": "Name #2",
-                "kind": TransactionLineItem.Kind.Credit,
-                "unit_amount": "45.1232",
-                "unit_of_measure": "1234567890123",
-                "discount_amount": "1.02",
-                "total_amount": "45.15",
-                "product_code": "23434",
-                "commodity_code": "9SAASSD8724",
-            }]
-        })
-
-        self.assertFalse(result.is_success)
-
-        self.assertEqual(
-            ErrorCodes.Transaction.LineItem.UnitOfMeasureIsTooLarge,
-            result.errors.for_object("transaction").for_object("line_items").for_object("index_1").on("unit_of_measure")[0].code
-        )
-
-    def test_sale_with_line_items_validation_error_unit_tax_amount_format_is_invalid(self):
-        result = Transaction.sale({
-            "amount": "35.05",
-            "credit_card": {
-                "number": CreditCardNumbers.Visa,
-                "expiration_date": "05/2009",
-            },
-            "line_items": [{
-                "quantity": "1.2322",
-                "name": "Name #1",
-                "kind": TransactionLineItem.Kind.Debit,
-                "unit_amount": "45.1232",
-                "unit_of_measure": "gallon",
-                "discount_amount": "1.02",
-                "total_amount": "45.15",
-                "product_code": "23434",
-                "commodity_code": "9SAASSD8724",
-            },
-            {
-                "quantity": "1.2322",
-                "name": "Name #2",
-                "kind": TransactionLineItem.Kind.Credit,
-                "unit_amount": "45.0122",
-                "unit_tax_amount": "2.012",
-                "unit_of_measure": "gallon",
-                "discount_amount": "1.02",
-                "total_amount": "45.15",
-                "product_code": "23434",
-                "commodity_code": "9SAASSD8724",
-            }]
-        })
-
-        self.assertFalse(result.is_success)
-
-        self.assertEqual(
-            ErrorCodes.Transaction.LineItem.UnitTaxAmountFormatIsInvalid,
-            result.errors.for_object("transaction").for_object("line_items").for_object("index_1").on("unit_tax_amount")[0].code
-        )
-
-    def test_sale_with_line_items_validation_error_unit_tax_amount_is_too_large(self):
-        result = Transaction.sale({
-            "amount": "35.05",
-            "credit_card": {
-                "number": CreditCardNumbers.Visa,
-                "expiration_date": "05/2009",
-            },
-            "line_items": [{
-                "quantity": "1.2322",
-                "name": "Name #1",
-                "kind": TransactionLineItem.Kind.Debit,
-                "unit_amount": "45.1232",
-                "unit_tax_amount": "1.23",
-                "unit_of_measure": "gallon",
-                "discount_amount": "1.02",
-                "total_amount": "45.15",
-                "product_code": "23434",
-                "commodity_code": "9SAASSD8724",
-            },
-            {
-                "quantity": "1.2322",
-                "name": "Name #2",
-                "kind": TransactionLineItem.Kind.Credit,
-                "unit_amount": "45.0122",
-                "unit_tax_amount": "2147483648",
-                "unit_of_measure": "gallon",
-                "discount_amount": "1.02",
-                "total_amount": "45.15",
-                "product_code": "23434",
-                "commodity_code": "9SAASSD8724",
-            }]
-        })
-
-        self.assertFalse(result.is_success)
-
-        self.assertEqual(
-            ErrorCodes.Transaction.LineItem.UnitTaxAmountIsTooLarge,
-            result.errors.for_object("transaction").for_object("line_items").for_object("index_1").on("unit_tax_amount")[0].code
-        )
-
-    def test_sale_with_line_items_validation_error_unit_tax_amount_cannot_be_negative(self):
-        result = Transaction.sale({
-            "amount": "35.05",
-            "credit_card": {
-                "number": CreditCardNumbers.Visa,
-                "expiration_date": "05/2009",
-            },
-            "line_items": [{
-                "quantity": "1.2322",
-                "name": "Name #1",
-                "kind": TransactionLineItem.Kind.Debit,
-                "unit_amount": "45.1232",
-                "unit_of_measure": "gallon",
-                "discount_amount": "1.02",
-                "total_amount": "45.15",
-                "product_code": "23434",
-                "commodity_code": "9SAASSD8724",
-            },
-            {
-                "quantity": "1.2322",
-                "name": "Name #2",
-                "kind": TransactionLineItem.Kind.Credit,
-                "unit_amount": "45.0122",
-                "unit_tax_amount": "-1.23",
-                "unit_of_measure": "gallon",
-                "discount_amount": "1.02",
-                "total_amount": "45.15",
-                "product_code": "23434",
-                "commodity_code": "9SAASSD8724",
-            }]
-        })
-
-        self.assertFalse(result.is_success)
-
-        self.assertEqual(
-            ErrorCodes.Transaction.LineItem.UnitTaxAmountCannotBeNegative,
-            result.errors.for_object("transaction").for_object("line_items").for_object("index_1").on("unit_tax_amount")[0].code
-        )
-
-    def test_sale_with_line_items_validation_error_too_many_live_items(self):
-        sale_params = {
-            "amount": "35.05",
-            "credit_card": {
-                "number": CreditCardNumbers.Visa,
-                "expiration_date": "05/2009",
-            },
-            "line_items": [],
-        }
-
-        for i in range(250):
-            sale_params["line_items"].append({
-                "quantity": "2.02",
-                "name": "Line item #" + str(i),
-                "kind": TransactionLineItem.Kind.Credit,
-                "unit_amount": "5",
-                "unit_of_measure": "gallon",
-                "total_amount": "10.1",
-            })
-
-        result = Transaction.sale(sale_params)
-
-        self.assertFalse(result.is_success)
-
-        self.assertEqual(
-            ErrorCodes.Transaction.TooManyLineItems,
-            result.errors.for_object("transaction").on("line_items")[0].code
-        )
-
-    def test_validation_error_on_invalid_custom_fields(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            },
-            "custom_fields": {
-                "invalid_key": "some extra stuff"
-            }
-
-        })
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(
-            ErrorCodes.Transaction.CustomFieldIsInvalid,
-            result.errors.for_object("transaction").on("custom_fields")[0].code
-        )
-
-    def test_card_type_indicators(self):
-        result = Transaction.sale({
-            "amount": Decimal(TransactionAmounts.Authorize),
-            "credit_card": {
-                "number": CreditCardNumbers.CardTypeIndicators.Unknown,
-                "expiration_month": "05",
-                "expiration_year": "2012"
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-        self.assertEqual(CreditCard.Prepaid.Unknown, transaction.credit_card_details.prepaid)
-        self.assertEqual(CreditCard.Debit.Unknown, transaction.credit_card_details.debit)
-        self.assertEqual(CreditCard.Commercial.Unknown, transaction.credit_card_details.commercial)
-        self.assertEqual(CreditCard.Healthcare.Unknown, transaction.credit_card_details.healthcare)
-        self.assertEqual(CreditCard.Payroll.Unknown, transaction.credit_card_details.payroll)
-        self.assertEqual(CreditCard.DurbinRegulated.Unknown, transaction.credit_card_details.durbin_regulated)
-        self.assertEqual(CreditCard.CardTypeIndicator.Unknown, transaction.credit_card_details.issuing_bank)
-        self.assertEqual(CreditCard.CardTypeIndicator.Unknown, transaction.credit_card_details.country_of_issuance)
-        self.assertEqual(CreditCard.ProductId.Unknown, transaction.credit_card_details.product_id)
-
-    def test_create_can_set_recurring_flag(self):
-        result = Transaction.sale({
-            "amount": "100",
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            },
-            "recurring": True
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-        self.assertEqual(True, transaction.recurring)
-
-    def test_create_can_set_transaction_source_flag_recurring_first(self):
-        result = Transaction.sale({
-            "amount": "100",
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            },
-            "transaction_source": "recurring_first"
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-        self.assertEqual(True, transaction.recurring)
-
-    def test_create_can_set_transaction_source_flag_recurring(self):
-        result = Transaction.sale({
-            "amount": "100",
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            },
-            "transaction_source": "recurring"
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-        self.assertEqual(True, transaction.recurring)
-
-    def test_create_can_set_transaction_source_flag_merchant(self):
-        result = Transaction.sale({
-            "amount": "100",
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            },
-            "transaction_source": "merchant"
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-        self.assertEqual(False, transaction.recurring)
-
-    def test_create_can_set_transaction_source_flag_moto(self):
-        result = Transaction.sale({
-            "amount": "100",
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            },
-            "transaction_source": "moto"
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-        self.assertEqual(False, transaction.recurring)
-
-    def test_create_can_set_transaction_source_flag_invalid(self):
-        result = Transaction.sale({
-            "amount": "100",
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            },
-            "transaction_source": "invalid_value"
-        })
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(
-            ErrorCodes.Transaction.TransactionSourceIsInvalid,
-            result.errors.for_object("transaction").on("transaction_source")[0].code
-        )
-
-    def test_create_can_store_customer_and_credit_card_in_the_vault(self):
-        result = Transaction.sale({
-            "amount": "100",
-            "customer": {
-                "first_name": "Adam",
-                "last_name": "Williams"
-            },
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            },
-            "options": {
-                "store_in_vault": True
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-        self.assertNotEqual(None, re.search(r"\A\d{6,}\Z", transaction.customer_details.id))
-        self.assertEqual(transaction.customer_details.id, transaction.vault_customer.id)
-        self.assertNotEqual(None, re.search(r"\A\w{4,}\Z", transaction.credit_card_details.token))
-        self.assertEqual(transaction.credit_card_details.token, transaction.vault_credit_card.token)
-
-    def test_create_can_store_customer_and_credit_card_in_the_vault_on_success(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "customer": {
-                "first_name": "Adam",
-                "last_name": "Williams"
-            },
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            },
-            "options": {
-                "store_in_vault_on_success": True
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-        self.assertNotEqual(None, re.search(r"\A\d{6,}\Z", transaction.customer_details.id))
-        self.assertEqual(transaction.customer_details.id, transaction.vault_customer.id)
-        self.assertNotEqual(None, re.search(r"\A\w{4,}\Z", transaction.credit_card_details.token))
-        self.assertEqual(transaction.credit_card_details.token, transaction.vault_credit_card.token)
-
-    def test_create_does_not_store_customer_and_credit_card_in_the_vault_on_failure(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Decline,
-            "customer": {
-                "first_name": "Adam",
-                "last_name": "Williams"
-            },
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            },
-            "options": {
-                "store_in_vault_on_success": True
-            }
-        })
-
-        self.assertFalse(result.is_success)
-        transaction = result.transaction
-        self.assertEqual(None, transaction.customer_details.id)
-        self.assertEqual(None, transaction.credit_card_details.token)
-        self.assertEqual(None, transaction.vault_customer)
-        self.assertEqual(None, transaction.vault_credit_card)
-
-    def test_create_associated_a_billing_address_with_credit_card_in_vault(self):
-        result = Transaction.sale({
-            "amount": "100",
-            "customer": {
-                "first_name": "Adam",
-                "last_name": "Williams"
-            },
-            "credit_card": {
-                "number": "5105105105105100",
-                "expiration_date": "05/2012"
-            },
-            "billing": {
-                "first_name": "Carl",
-                "last_name": "Jones",
-                "company": "Braintree",
-                "street_address": "123 E Main St",
-                "extended_address": "Suite 403",
-                "locality": "Chicago",
-                "region": "IL",
-                "postal_code": "60622",
-                "country_name": "United States of America"
-            },
-            "options": {
-                "store_in_vault": True,
-                "add_billing_address_to_payment_method": True,
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-        self.assertNotEqual(None, re.search(r"\A\d{6,}\Z", transaction.customer_details.id))
-        self.assertEqual(transaction.customer_details.id, transaction.vault_customer.id)
-        credit_card = CreditCard.find(transaction.vault_credit_card.token)
-        self.assertEqual(credit_card.billing_address.id, transaction.billing_details.id)
-        self.assertEqual(credit_card.billing_address.id, transaction.vault_billing_address.id)
-        self.assertEqual("Carl", credit_card.billing_address.first_name)
-        self.assertEqual("Jones", credit_card.billing_address.last_name)
-        self.assertEqual("Braintree", credit_card.billing_address.company)
-        self.assertEqual("123 E Main St", credit_card.billing_address.street_address)
-        self.assertEqual("Suite 403", credit_card.billing_address.extended_address)
-        self.assertEqual("Chicago", credit_card.billing_address.locality)
-        self.assertEqual("IL", credit_card.billing_address.region)
-        self.assertEqual("60622", credit_card.billing_address.postal_code)
-        self.assertEqual("United States of America", credit_card.billing_address.country_name)
-
-    def test_create_and_store_the_shipping_address_in_the_vault(self):
-        result = Transaction.sale({
-            "amount": "100",
-            "customer": {
-                "first_name": "Adam",
-                "last_name": "Williams"
-            },
-            "credit_card": {
-                "number": "5105105105105100",
-                "expiration_date": "05/2012"
-            },
-            "shipping": {
-                "first_name": "Carl",
-                "last_name": "Jones",
-                "company": "Braintree",
-                "street_address": "123 E Main St",
-                "extended_address": "Suite 403",
-                "locality": "Chicago",
-                "region": "IL",
-                "postal_code": "60622",
-                "country_name": "United States of America"
-            },
-            "options": {
-                "store_in_vault": True,
-                "store_shipping_address_in_vault": True,
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-        self.assertNotEqual(None, re.search(r"\A\d{6,}\Z", transaction.customer_details.id))
-        self.assertEqual(transaction.customer_details.id, transaction.vault_customer.id)
-        shipping_address = transaction.vault_customer.addresses[0]
-        self.assertEqual("Carl", shipping_address.first_name)
-        self.assertEqual("Jones", shipping_address.last_name)
-        self.assertEqual("Braintree", shipping_address.company)
-        self.assertEqual("123 E Main St", shipping_address.street_address)
-        self.assertEqual("Suite 403", shipping_address.extended_address)
-        self.assertEqual("Chicago", shipping_address.locality)
-        self.assertEqual("IL", shipping_address.region)
-        self.assertEqual("60622", shipping_address.postal_code)
-        self.assertEqual("United States of America", shipping_address.country_name)
-
-    def test_create_submits_for_settlement_if_given_submit_for_settlement_option(self):
-        result = Transaction.sale({
-            "amount": "100",
-            "credit_card": {
-                "number": "5105105105105100",
-                "expiration_date": "05/2012"
-            },
-            "options": {
-                "submit_for_settlement": True
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertEqual(Transaction.Status.SubmittedForSettlement, result.transaction.status)
-
-    def test_create_does_not_submit_for_settlement_if_submit_for_settlement_is_false(self):
-        result = Transaction.sale({
-            "amount": "100",
-            "credit_card": {
-                "number": "5105105105105100",
-                "expiration_date": "05/2012"
-            },
-            "options": {
-                "submit_for_settlement": False
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertEqual(Transaction.Status.Authorized, result.transaction.status)
-
-    def test_create_can_specify_the_customer_id_and_payment_method_token(self):
-        customer_id = "customer_" + str(random.randint(1, 1000000))
-        payment_method_token = "credit_card_" + str(random.randint(1, 1000000))
-
-        result = Transaction.sale({
-            "amount": "100",
-            "customer": {
-              "id": customer_id,
-              "first_name": "Adam",
-              "last_name": "Williams"
-            },
-            "credit_card": {
-              "token": payment_method_token,
-              "number": "5105105105105100",
-              "expiration_date": "05/2012"
-            },
-            "options": {
-              "store_in_vault": True
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-        self.assertEqual(customer_id, transaction.customer_details.id)
-        self.assertEqual(customer_id, transaction.vault_customer.id)
-        self.assertEqual(payment_method_token, transaction.credit_card_details.token)
-        self.assertEqual(payment_method_token, transaction.vault_credit_card.token)
-
-    def test_create_using_customer_id(self):
-        result = Customer.create({
-            "first_name": "Mike",
-            "last_name": "Jones",
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2010",
-                "cvv": "100"
-            }
-        })
-        self.assertTrue(result.is_success)
-        customer = result.customer
-        credit_card = customer.credit_cards[0]
-
-        result = Transaction.sale({
-            "amount": "100",
-            "customer_id": customer.id
-        })
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-        self.assertEqual(customer.id, transaction.customer_details.id)
-        self.assertEqual(customer.id, transaction.vault_customer.id)
-        self.assertEqual(credit_card.token, transaction.credit_card_details.token)
-        self.assertEqual(credit_card.token, transaction.vault_credit_card.token)
-
-    def test_create_using_payment_method_token(self):
-        result = Customer.create({
-            "first_name": "Mike",
-            "last_name": "Jones",
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2010",
-                "cvv": "100"
-            }
-        })
-        self.assertTrue(result.is_success)
-        customer = result.customer
-        credit_card = customer.credit_cards[0]
-
-        result = Transaction.sale({
-            "amount": "100",
-            "payment_method_token": credit_card.token
-        })
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-        self.assertEqual(customer.id, transaction.customer_details.id)
-        self.assertEqual(customer.id, transaction.vault_customer.id)
-        self.assertEqual(credit_card.token, transaction.credit_card_details.token)
-        self.assertEqual(credit_card.token, transaction.vault_credit_card.token)
-
-    def test_create_using_payment_method_token_with_cvv(self):
-        result = Customer.create({
-            "first_name": "Mike",
-            "last_name": "Jones",
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2010",
-                "cvv": "100"
-            }
-        })
-        self.assertTrue(result.is_success)
-        customer = result.customer
-        credit_card = customer.credit_cards[0]
-
-        result = Transaction.sale({
-            "amount": "100",
-            "payment_method_token": credit_card.token,
-            "credit_card": {
-                "cvv": "301"
-            }
-        })
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-        self.assertEqual(customer.id, transaction.customer_details.id)
-        self.assertEqual(customer.id, transaction.vault_customer.id)
-        self.assertEqual(credit_card.token, transaction.credit_card_details.token)
-        self.assertEqual(credit_card.token, transaction.vault_credit_card.token)
-        self.assertEqual("S", transaction.cvv_response_code)
-
-    def test_create_with_failing_validations(self):
-        params = {
-            "transaction": {
-                "amount": None,
-                "credit_card": {
-                    "number": "4111111111111111",
-                    "expiration_date": "05/2009"
-                }
-            }
-        }
-        result = Transaction.sale(params["transaction"])
-        params["transaction"]["credit_card"].pop("number")
-        self.assertFalse(result.is_success)
-        self.assertEqual(params, result.params)
-        self.assertEqual(
-            ErrorCodes.Transaction.AmountIsRequired,
-            result.errors.for_object("transaction").on("amount")[0].code
-        )
-
-    def test_credit_with_a_successful_result(self):
-        result = Transaction.credit({
-            "amount": Decimal(TransactionAmounts.Authorize),
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-        self.assertNotEqual(None, re.search(r"\A\w{6,}\Z", transaction.id))
-        self.assertEqual(Transaction.Type.Credit, transaction.type)
-        self.assertEqual(Decimal(TransactionAmounts.Authorize), transaction.amount)
-        cc_details = transaction.credit_card_details
-        self.assertEqual("411111", cc_details.bin)
-        self.assertEqual("1111", cc_details.last_4)
-        self.assertEqual("05/2009", cc_details.expiration_date)
-
-    def test_credit_with_unsuccessful_result(self):
-        result = Transaction.credit({
-            "amount": None,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            }
-        })
-
-        params = {
-            "transaction": {
-                "type": Transaction.Type.Credit,
-                "amount": None,
-                "credit_card": {
-                    "expiration_date": "05/2009"
-                }
-            }
-        }
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(params, result.params)
-        self.assertEqual(
-            ErrorCodes.Transaction.AmountIsRequired,
-            result.errors.for_object("transaction").on("amount")[0].code
-        )
-
-    def test_credit_card_payment_instrument_type_is_credit_card(self):
-        result = Transaction.credit({
-            "amount": Decimal(TransactionAmounts.Authorize),
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-        self.assertEqual(
-            PaymentInstrumentType.CreditCard,
-            transaction.payment_instrument_type
-        )
-
-    def test_service_fee_not_allowed_with_credits(self):
-        result = Transaction.credit({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            },
-            "service_fee_amount": "1.00"
-        })
-
-        self.assertFalse(result.is_success)
-        self.assertTrue(
-            ErrorCodes.Transaction.ServiceFeeIsNotAllowedOnCredits in [error.code for error in result.errors.for_object("transaction").on("base")]
-        )
-
-    def test_credit_with_merchant_account_id(self):
-        result = Transaction.credit({
-            "amount": TransactionAmounts.Authorize,
-            "merchant_account_id": TestHelper.non_default_merchant_account_id,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-        self.assertEqual(TestHelper.non_default_merchant_account_id, transaction.merchant_account_id)
-
-    def test_credit_without_merchant_account_id_falls_back_to_default(self):
-        result = Transaction.credit({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-        self.assertEqual(TestHelper.default_merchant_account_id, transaction.merchant_account_id)
-
-    def test_find_returns_a_found_transaction(self):
-        transaction = Transaction.sale({
-            "amount": TransactionAmounts.Fail,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            }
-        }).transaction
-        TestHelper.settle_transaction(transaction.id)
-        found_transaction = Transaction.find(transaction.id)
-        self.assertEqual(transaction.id, found_transaction.id)
-
-    @raises_with_regexp(NotFoundError, "transaction with id 'notreal' not found")
-    def test_find_for_bad_transaction_raises_not_found_error(self):
-        Transaction.find("notreal")
-
-    def test_void_with_successful_result(self):
-        transaction = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            }
-        }).transaction
-
-        result = Transaction.void(transaction.id)
-        self.assertTrue(result.is_success)
-        self.assertEqual(transaction.id, result.transaction.id)
-        self.assertEqual(Transaction.Status.Voided, result.transaction.status)
-
-    def test_void_with_unsuccessful_result(self):
-        transaction = Transaction.sale({
-            "amount": TransactionAmounts.Decline,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            }
-        }).transaction
-
-        result = Transaction.void(transaction.id)
-        self.assertFalse(result.is_success)
-        self.assertEqual(
-            ErrorCodes.Transaction.CannotBeVoided,
-            result.errors.for_object("transaction").on("base")[0].code
-        )
-
-    def test_create_with_successful_result(self):
-        result = Transaction.create({
-            "type": Transaction.Type.Sale,
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-        self.assertEqual(Transaction.Type.Sale, transaction.type)
-
-    def test_create_with_error_result(self):
-        result = Transaction.create({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            },
-            "billing": {
-                "country_code_alpha2": "ZZ",
-                "country_code_alpha3": "ZZZ",
-                "country_code_numeric": "000",
-                "country_name": "zzzzzz"
-            }
-        })
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(ErrorCodes.Transaction.TypeIsRequired, result.errors.for_object("transaction").on("type")[0].code)
-        self.assertEqual(
-            ErrorCodes.Address.CountryCodeAlpha2IsNotAccepted,
-            result.errors.for_object("transaction").for_object("billing").on("country_code_alpha2")[0].code
-        )
-        self.assertEqual(
-            ErrorCodes.Address.CountryCodeAlpha3IsNotAccepted,
-            result.errors.for_object("transaction").for_object("billing").on("country_code_alpha3")[0].code
-        )
-        self.assertEqual(
-            ErrorCodes.Address.CountryCodeNumericIsNotAccepted,
-            result.errors.for_object("transaction").for_object("billing").on("country_code_numeric")[0].code
-        )
-        self.assertEqual(
-            ErrorCodes.Address.CountryNameIsNotAccepted,
-            result.errors.for_object("transaction").for_object("billing").on("country_name")[0].code
-        )
-
-    def test_sale_from_transparent_redirect_with_successful_result(self):
-        tr_data = {
-            "transaction": {
-                "amount": TransactionAmounts.Authorize,
-            }
-        }
-        post_params = {
-            "tr_data": Transaction.tr_data_for_sale(tr_data, "http://example.com/path"),
-            "transaction[credit_card][number]": "4111111111111111",
-            "transaction[credit_card][expiration_date]": "05/2010",
-        }
-
-        query_string = TestHelper.simulate_tr_form_post(post_params, Transaction.transparent_redirect_create_url())
-        result = Transaction.confirm_transparent_redirect(query_string)
-        self.assertTrue(result.is_success)
-
-        transaction = result.transaction
-        self.assertEqual(Decimal(TransactionAmounts.Authorize), transaction.amount)
-        self.assertEqual(Transaction.Type.Sale, transaction.type)
-        self.assertEqual("411111", transaction.credit_card_details.bin)
-        self.assertEqual("1111", transaction.credit_card_details.last_4)
-        self.assertEqual("05/2010", transaction.credit_card_details.expiration_date)
-
-    def test_sale_from_transparent_redirect_with_error_result(self):
-        tr_data = {
-            "transaction": {
-                "amount": TransactionAmounts.Authorize,
-            }
-        }
-        post_params = {
-            "tr_data": Transaction.tr_data_for_sale(tr_data, "http://example.com/path"),
-            "transaction[credit_card][number]": "booya",
-            "transaction[credit_card][expiration_date]": "05/2010",
-        }
-
-        query_string = TestHelper.simulate_tr_form_post(post_params, Transaction.transparent_redirect_create_url())
-        result = Transaction.confirm_transparent_redirect(query_string)
-        self.assertFalse(result.is_success)
-        self.assertTrue(len(result.errors.for_object("transaction").for_object("credit_card").on("number")) > 0)
-
-    def test_sale_from_transparent_redirect_with_403_and_message(self):
-        tr_data = {
-            "transaction": {
-                "amount": TransactionAmounts.Authorize
-            }
-        }
-        post_params = {
-            "tr_data": Transaction.tr_data_for_sale(tr_data, "http://example.com/path"),
-            "transaction[credit_card][number]": "booya",
-            "transaction[credit_card][expiration_date]": "05/2010",
-            "transaction[bad]": "value"
-        }
-
-        query_string = TestHelper.simulate_tr_form_post(post_params, Transaction.transparent_redirect_create_url())
-        try:
-            Transaction.confirm_transparent_redirect(query_string)
-            self.fail()
-        except AuthorizationError as e:
-            self.assertEqual("Invalid params: transaction[bad]", str(e))
-
-    def test_credit_from_transparent_redirect_with_successful_result(self):
-        tr_data = {
-            "transaction": {
-                "amount": TransactionAmounts.Authorize,
-            }
-        }
-        post_params = {
-            "tr_data": Transaction.tr_data_for_credit(tr_data, "http://example.com/path"),
-            "transaction[credit_card][number]": "4111111111111111",
-            "transaction[credit_card][expiration_date]": "05/2010",
-            "transaction[billing][country_code_alpha2]": "US",
-            "transaction[billing][country_code_alpha3]": "USA",
-            "transaction[billing][country_code_numeric]": "840",
-            "transaction[billing][country_name]": "United States of America"
-        }
-
-        query_string = TestHelper.simulate_tr_form_post(post_params, Transaction.transparent_redirect_create_url())
-        result = Transaction.confirm_transparent_redirect(query_string)
-        self.assertTrue(result.is_success)
-
-        transaction = result.transaction
-        self.assertEqual(Decimal(TransactionAmounts.Authorize), transaction.amount)
-        self.assertEqual(Transaction.Type.Credit, transaction.type)
-        self.assertEqual("411111", transaction.credit_card_details.bin)
-        self.assertEqual("1111", transaction.credit_card_details.last_4)
-        self.assertEqual("05/2010", transaction.credit_card_details.expiration_date)
-
-        self.assertEqual("US", transaction.billing_details.country_code_alpha2)
-        self.assertEqual("USA", transaction.billing_details.country_code_alpha3)
-        self.assertEqual("840", transaction.billing_details.country_code_numeric)
-        self.assertEqual("United States of America", transaction.billing_details.country_name)
-
-    def test_credit_from_transparent_redirect_with_error_result(self):
-        tr_data = {
-            "transaction": {
-                "amount": TransactionAmounts.Authorize,
-            }
-        }
-        post_params = {
-            "tr_data": Transaction.tr_data_for_credit(tr_data, "http://example.com/path"),
-            "transaction[credit_card][number]": "booya",
-            "transaction[credit_card][expiration_date]": "05/2010",
-        }
-
-        query_string = TestHelper.simulate_tr_form_post(post_params, Transaction.transparent_redirect_create_url())
-        result = Transaction.confirm_transparent_redirect(query_string)
-        self.assertFalse(result.is_success)
-        self.assertTrue(len(result.errors.for_object("transaction").for_object("credit_card").on("number")) > 0)
-
-    def test_submit_for_settlement_without_amount(self):
-        transaction = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            }
-        }).transaction
-
-        submitted_transaction = Transaction.submit_for_settlement(transaction.id).transaction
-
-        self.assertEqual(Transaction.Status.SubmittedForSettlement, submitted_transaction.status)
-        self.assertEqual(Decimal(TransactionAmounts.Authorize), submitted_transaction.amount)
-
-    def test_submit_for_settlement_with_amount(self):
-        transaction = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            }
-        }).transaction
-
-        submitted_transaction = Transaction.submit_for_settlement(transaction.id, Decimal("900")).transaction
-
-        self.assertEqual(Transaction.Status.SubmittedForSettlement, submitted_transaction.status)
-        self.assertEqual(Decimal("900.00"), submitted_transaction.amount)
-
-    def test_submit_for_settlement_with_order_id(self):
-        transaction = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            }
-        }).transaction
-
-        params = {"order_id": "ABC123"}
-
-        submitted_transaction = Transaction.submit_for_settlement(transaction.id, Decimal("900"), params).transaction
-
-        self.assertEqual(Transaction.Status.SubmittedForSettlement, submitted_transaction.status)
-        self.assertEqual("ABC123", submitted_transaction.order_id)
-
-    def test_submit_for_settlement_with_descriptor(self):
-        transaction = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            }
-        }).transaction
-
-        params = {
-            "descriptor": {
-                "name": "123*123456789012345678",
-                "phone": "3334445555",
-                "url": "ebay.com"
-            }
-        }
-
-        submitted_transaction = Transaction.submit_for_settlement(transaction.id, Decimal("900"), params).transaction
-
-        self.assertEqual(Transaction.Status.SubmittedForSettlement, submitted_transaction.status)
-        self.assertEqual("123*123456789012345678", submitted_transaction.descriptor.name)
-        self.assertEqual("3334445555", submitted_transaction.descriptor.phone)
-        self.assertEqual("ebay.com", submitted_transaction.descriptor.url)
-
-    @raises_with_regexp(KeyError, "'Invalid keys: invalid_param'")
-    def test_submit_for_settlement_with_invalid_params(self):
-        transaction = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            }
-        }).transaction
-
-        params = {
-            "descriptor": {
-                "name": "123*123456789012345678",
-                "phone": "3334445555",
-                "url": "ebay.com"
-            },
-            "invalid_param": "foo",
-        }
-
-        Transaction.submit_for_settlement(transaction.id, Decimal("900"), params)
-
-    def test_submit_for_settlement_with_validation_error(self):
-        transaction = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            }
-        }).transaction
-
-        result = Transaction.submit_for_settlement(transaction.id, Decimal("1200"))
-        self.assertFalse(result.is_success)
-
-        self.assertEqual(
-            ErrorCodes.Transaction.SettlementAmountIsTooLarge,
-            result.errors.for_object("transaction").on("amount")[0].code
-        )
-
-    def test_submit_for_settlement_with_validation_error_on_service_fee(self):
-        transaction = Transaction.sale({
-            "amount": "10.00",
-            "merchant_account_id": TestHelper.non_default_sub_merchant_account_id,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            },
-            "service_fee_amount": "5.00"
-        }).transaction
-
-        result = Transaction.submit_for_settlement(transaction.id, "1.00")
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(
-            ErrorCodes.Transaction.SettlementAmountIsLessThanServiceFeeAmount,
-            result.errors.for_object("transaction").on("amount")[0].code
-        )
-
-    def test_update_details_with_valid_params(self):
-        transaction = Transaction.sale({
-            "amount": "10.00",
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            },
-            "options": {
-                "submit_for_settlement": True
-            }
-        }).transaction
-
-        params = {
-            "amount" : "9.00",
-            "order_id": "123",
-            "descriptor": {
-                "name": "456*123456789012345678",
-                "phone": "3334445555",
-                "url": "ebay.com"
-            }
-        }
-
-        result = Transaction.update_details(transaction.id, params)
-        self.assertTrue(result.is_success)
-        self.assertEqual(Decimal("9.00"), result.transaction.amount)
-        self.assertEqual(Transaction.Status.SubmittedForSettlement, result.transaction.status)
-        self.assertEqual("123", result.transaction.order_id)
-        self.assertEqual("456*123456789012345678", result.transaction.descriptor.name)
-        self.assertEqual("3334445555", result.transaction.descriptor.phone)
-        self.assertEqual("ebay.com", result.transaction.descriptor.url)
-
-    @raises_with_regexp(KeyError, "'Invalid keys: invalid_key'")
-    def test_update_details_with_invalid_params(self):
-        transaction = Transaction.sale({
-            "amount": "10.00",
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            },
-            "options": {
-                "submit_for_settlement": True
-            },
-        }).transaction
-
-        params = {
-            "amount" : "9.00",
-            "invalid_key": "invalid_value",
-            "order_id": "123",
-            "descriptor": {
-                "name": "456*123456789012345678",
-                "phone": "3334445555",
-                "url": "ebay.com"
-            }
-        }
-
-        Transaction.update_details(transaction.id, params)
-
-    def test_update_details_with_invalid_order_id(self):
-        transaction = Transaction.sale({
-            "amount": "10.00",
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            },
-            "options": {
-                "submit_for_settlement": True
-            },
-        }).transaction
-
-        params = {
-            "amount" : "9.00",
-            "order_id": "A" * 256,
-            "descriptor": {
-                "name": "456*123456789012345678",
-                "phone": "3334445555",
-                "url": "ebay.com"
-            }
-        }
-
-        result = Transaction.update_details(transaction.id, params)
-        self.assertFalse(result.is_success)
-        self.assertEqual(
-            ErrorCodes.Transaction.OrderIdIsTooLong,
-            result.errors.for_object("transaction").on("order_id")[0].code
-        )
-
-    def test_update_details_with_invalid_descriptor(self):
-        transaction = Transaction.sale({
-            "amount": "10.00",
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            },
-            "options": {
-                "submit_for_settlement": True
-            },
-        }).transaction
-
-        params = {
-            "amount" : "9.00",
-            "order_id": "123",
-            "descriptor": {
-                "name": "invalid name",
-                "phone": "invalid phone",
-                "url": "12345678901234567890"
-            }
-        }
-
-        result = Transaction.update_details(transaction.id, params)
-        self.assertFalse(result.is_success)
-        self.assertEqual(
-            ErrorCodes.Descriptor.NameFormatIsInvalid,
-            result.errors.for_object("transaction").for_object("descriptor").on("name")[0].code
-        )
-        self.assertEqual(
-            ErrorCodes.Descriptor.PhoneFormatIsInvalid,
-            result.errors.for_object("transaction").for_object("descriptor").on("phone")[0].code
-        )
-        self.assertEqual(
-            ErrorCodes.Descriptor.UrlFormatIsInvalid,
-            result.errors.for_object("transaction").for_object("descriptor").on("url")[0].code
-        )
-
-    def test_update_details_with_invalid_amount(self):
-        transaction = Transaction.sale({
-            "amount": "10.00",
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            },
-            "options": {
-                "submit_for_settlement": True
-            },
-        }).transaction
-
-        params = {
-            "amount" : "999.00",
-            "order_id": "123",
-        }
-
-        result = Transaction.update_details(transaction.id, params)
-        self.assertFalse(result.is_success)
-        self.assertEqual(
-            ErrorCodes.Transaction.SettlementAmountIsTooLarge,
-            result.errors.for_object("transaction").on("amount")[0].code
-        )
-
-    def test_update_details_with_invalid_status(self):
-        transaction = Transaction.sale({
-            "amount": "10.00",
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            },
-        }).transaction
-
-        params = {
-            "amount" : "9.00",
-            "order_id": "123",
-        }
-
-        result = Transaction.update_details(transaction.id, params)
-        self.assertFalse(result.is_success)
-        self.assertEqual(
-            ErrorCodes.Transaction.CannotUpdateTransactionDetailsNotSubmittedForSettlement,
-            result.errors.for_object("transaction").on("base")[0].code
-        )
-
-    def test_update_details_with_invalid_processor(self):
-        transaction = Transaction.sale({
-            "amount": "10.00",
-            "merchant_account_id": TestHelper.fake_amex_direct_merchant_account_id,
-            "credit_card": {
-                "number": CreditCardNumbers.AmexPayWithPoints.Success,
-                "expiration_date": "05/2020"
-            },
-            "options": {
-                "submit_for_settlement": True
-            },
-        }).transaction
-
-        params = {
-            "amount" : "9.00",
-            "order_id": "123",
-        }
-
-        result = Transaction.update_details(transaction.id, params)
-        self.assertFalse(result.is_success)
-        self.assertEqual(
-            ErrorCodes.Transaction.ProcessorDoesNotSupportUpdatingTransactionDetails,
-            result.errors.for_object("transaction").on("base")[0].code
-        )
-
-    def test_status_history(self):
-        transaction = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            }
-        }).transaction
-
-        submitted_transaction = Transaction.submit_for_settlement(transaction.id).transaction
-
-        self.assertEqual(2, len(submitted_transaction.status_history))
-        self.assertEqual(Transaction.Status.Authorized, submitted_transaction.status_history[0].status)
-        self.assertEqual(Decimal(TransactionAmounts.Authorize), submitted_transaction.status_history[0].amount)
-        self.assertEqual(Transaction.Status.SubmittedForSettlement, submitted_transaction.status_history[1].status)
-        self.assertEqual(Decimal(TransactionAmounts.Authorize), submitted_transaction.status_history[1].amount)
-
-    def test_successful_refund(self):
-        transaction = self.__create_transaction_to_refund()
-
-        result = Transaction.refund(transaction.id)
-
-        self.assertTrue(result.is_success)
-        refund = result.transaction
-
-        self.assertEqual(Transaction.Type.Credit, refund.type)
-        self.assertEqual(Decimal(TransactionAmounts.Authorize), refund.amount)
-        self.assertEqual(transaction.id, refund.refunded_transaction_id)
-
-        self.assertEqual(refund.id, Transaction.find(transaction.id).refund_id)
-
-    def test_successful_partial_refund(self):
-        transaction = self.__create_transaction_to_refund()
-
-        result = Transaction.refund(transaction.id, Decimal("500.00"))
-
-        self.assertTrue(result.is_success)
-        self.assertEqual(Transaction.Type.Credit, result.transaction.type)
-        self.assertEqual(Decimal("500.00"), result.transaction.amount)
-
-    def test_multiple_successful_partial_refunds(self):
-        transaction = self.__create_transaction_to_refund()
-
-        refund1 = Transaction.refund(transaction.id, Decimal("500.00")).transaction
-        self.assertEqual(Transaction.Type.Credit, refund1.type)
-        self.assertEqual(Decimal("500.00"), refund1.amount)
-
-        refund2 = Transaction.refund(transaction.id, Decimal("500.00")).transaction
-        self.assertEqual(Transaction.Type.Credit, refund2.type)
-        self.assertEqual(Decimal("500.00"), refund2.amount)
-
-        transaction = Transaction.find(transaction.id)
-
-        self.assertEqual(2, len(transaction.refund_ids))
-        self.assertTrue(TestHelper.in_list(transaction.refund_ids, refund1.id))
-        self.assertTrue(TestHelper.in_list(transaction.refund_ids, refund2.id))
-
-    def test_refund_already_refunded_transation_fails(self):
-        transaction = self.__create_transaction_to_refund()
-
-        Transaction.refund(transaction.id)
-        result = Transaction.refund(transaction.id)
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(
-            ErrorCodes.Transaction.HasAlreadyBeenRefunded,
-            result.errors.for_object("transaction").on("base")[0].code
-        )
-
-    def test_refund_with_options_params(self):
-        transaction = self.__create_transaction_to_refund()
-        options = {
-            "amount": Decimal("1.00"),
-            "order_id": "abcd"
-        }
-        result = Transaction.refund(transaction.id, options)
-
-        self.assertTrue(result.is_success)
-        self.assertEqual(
-            "abcd",
-            result.transaction.order_id
-        )
-        self.assertEqual(
-            Decimal("1.00"),
-            result.transaction.amount
-        )
-
-    def test_refund_returns_an_error_if_unsettled(self):
-        transaction = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            },
-            "options": {
-                "submit_for_settlement": True
-            }
-        }).transaction
-
-        result = Transaction.refund(transaction.id)
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(
-            ErrorCodes.Transaction.CannotRefundUnlessSettled,
-            result.errors.for_object("transaction").on("base")[0].code
-        )
-
-    @staticmethod
-    def __create_transaction_to_refund():
-        transaction = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            },
-            "options": {
-                "submit_for_settlement": True
-            }
-        }).transaction
-
-        TestHelper.settle_transaction(transaction.id)
-        return transaction
-
-    @staticmethod
-    def __create_paypal_transaction():
-        transaction = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "payment_method_nonce": Nonces.PayPalOneTimePayment,
-            "options": {
-                "submit_for_settlement": True
-            }
-        }).transaction
-
-        return transaction
-
-    @staticmethod
-    def __create_escrowed_transaction():
-        transaction = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2012"
-            },
-            "service_fee_amount": "10.00",
-            "merchant_account_id": TestHelper.non_default_sub_merchant_account_id,
-            "options": {
-                "hold_in_escrow": True
-            }
-        }).transaction
-
-        TestHelper.escrow_transaction(transaction.id)
-        return transaction
-
-    def test_snapshot_plan_id_add_ons_and_discounts_from_subscription(self):
-        credit_card = Customer.create({
-            "first_name": "Mike",
-            "last_name": "Jones",
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2010",
-                "cvv": "100"
-            }
-        }).customer.credit_cards[0]
-
-        result = Subscription.create({
-            "payment_method_token": credit_card.token,
-            "plan_id": TestHelper.trialless_plan["id"],
-            "add_ons": {
-                "add": [
-                    {
-                        "amount": Decimal("11.00"),
-                        "inherited_from_id": "increase_10",
-                        "quantity": 2,
-                        "number_of_billing_cycles": 5
-                    },
-                    {
-                        "amount": Decimal("21.00"),
-                        "inherited_from_id": "increase_20",
-                        "quantity": 3,
-                        "number_of_billing_cycles": 6
-                    }
-                ]
-            },
-            "discounts": {
-                "add": [
-                    {
-                        "amount": Decimal("7.50"),
-                        "inherited_from_id": "discount_7",
-                        "quantity": 2,
-                        "never_expires": True
-                    }
-                ]
-            }
-        })
-
-        transaction = result.subscription.transactions[0]
-
-        self.assertEqual(TestHelper.trialless_plan["id"], transaction.plan_id)
-
-        self.assertEqual(2, len(transaction.add_ons))
-        add_ons = sorted(transaction.add_ons, key=lambda add_on: add_on.id)
-
-        self.assertEqual("increase_10", add_ons[0].id)
-        self.assertEqual(Decimal("11.00"), add_ons[0].amount)
-        self.assertEqual(2, add_ons[0].quantity)
-        self.assertEqual(5, add_ons[0].number_of_billing_cycles)
-        self.assertFalse(add_ons[0].never_expires)
-
-        self.assertEqual("increase_20", add_ons[1].id)
-        self.assertEqual(Decimal("21.00"), add_ons[1].amount)
-        self.assertEqual(3, add_ons[1].quantity)
-        self.assertEqual(6, add_ons[1].number_of_billing_cycles)
-        self.assertFalse(add_ons[1].never_expires)
-
-        self.assertEqual(1, len(transaction.discounts))
-        discounts = transaction.discounts
-
-        self.assertEqual("discount_7", discounts[0].id)
-        self.assertEqual(Decimal("7.50"), discounts[0].amount)
-        self.assertEqual(2, discounts[0].quantity)
-        self.assertEqual(None, discounts[0].number_of_billing_cycles)
-        self.assertTrue(discounts[0].never_expires)
-
-
-    def test_transactions_accept_lodging_industry_data(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            },
-            "industry": {
-                "industry_type": Transaction.IndustryType.Lodging,
-                "data": {
-                    "folio_number": "aaa",
-                    "check_in_date": "2014-07-07",
-                    "check_out_date": "2014-07-11",
-                    "room_rate": "170.00",
-                    "room_tax": "30.00",
-                    "no_show": False,
-                    "advanced_deposit": False,
-                    "fire_safe": True,
-                    "property_phone": "1112223345",
-                    "additional_charges": [
-                            {
-                                "kind": Transaction.AdditionalCharge.Restaurant,
-                                "amount": "50.00"
-                            },
-                            {
-                                "kind": Transaction.AdditionalCharge.Other,
-                                "amount": "150.00"
-                            }
-                        ]
-                    }
-                }
-            })
-
-        self.assertTrue(result.is_success)
-
-    def test_transactions_return_validation_errors_on_lodging_industry_data(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            },
-            "industry": {
-                "industry_type": Transaction.IndustryType.Lodging,
-                "data": {
-                    "folio_number": "aaa",
-                    "check_in_date": "2014-07-07",
-                    "check_out_date": "2014-06-06",
-                    "room_rate": "asdfsdf",
-                    "additional_charges": [
-                        {
-                            "kind": "unknown",
-                            "amount": "20.00"
-                        }
-                    ]
-                }
-            }
-        })
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(
-            ErrorCodes.Transaction.Industry.Lodging.CheckOutDateMustFollowCheckInDate,
-            result.errors.for_object("transaction").for_object("industry").on("check_out_date")[0].code
-        )
-        self.assertEqual(
-            ErrorCodes.Transaction.Industry.Lodging.RoomRateFormatIsInvalid,
-            result.errors.for_object("transaction").for_object("industry").on("room_rate")[0].code
-        )
-        self.assertEqual(
-            ErrorCodes.Transaction.Industry.AdditionalCharge.KindIsInvalid,
-            result.errors.for_object("transaction").for_object("industry").for_object("additional_charges").for_object("index_0").on("kind")[0].code
-        )
-
-    def test_transactions_accept_travel_cruise_industry_data(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            },
-            "industry": {
-                "industry_type": Transaction.IndustryType.TravelAndCruise,
-                "data": {
-                    "travel_package": "flight",
-                    "departure_date": "2014-07-07",
-                    "lodging_check_in_date": "2014-07-07",
-                    "lodging_check_out_date": "2014-09-07",
-                    "lodging_name": "Royal Caribbean"
-                }
-            }
-        })
-
-        self.assertTrue(result.is_success)
-
-    def test_transactions_return_validation_errors_on_travel_cruise_industry_data(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            },
-            "industry": {
-                "industry_type": Transaction.IndustryType.TravelAndCruise,
-                "data": {
-                    "travel_package": "roadtrip",
-                    "departure_date": "2014-07-07",
-                    "lodging_check_in_date": "2014-07-07",
-                    "lodging_check_out_date": "2014-09-07",
-                    "lodging_name": "Royal Caribbean"
-                }
-            }
-        })
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(
-            ErrorCodes.Transaction.Industry.TravelCruise.TravelPackageIsInvalid,
-            result.errors.for_object("transaction").for_object("industry").on("travel_package")[0].code
-        )
-
-    def test_transactions_accept_travel_flight_industry_data(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "payment_method_nonce": Nonces.PayPalOneTimePayment,
-            "options": {"submit_for_settlement": True},
-            "industry": {
-                "industry_type": Transaction.IndustryType.TravelAndFlight,
-                "data": {
-                    "passenger_first_name": "John",
-                    "passenger_last_name": "Doe",
-                    "passenger_middle_initial": "M",
-                    "passenger_title": "Mr.",
-                    "issued_date": date(2018, 1, 1),
-                    "travel_agency_name": "Expedia",
-                    "travel_agency_code": "12345678",
-                    "ticket_number": "ticket-number",
-                    "issuing_carrier_code": "AA",
-                    "customer_code": "customer-code",
-                    "fare_amount": "70.00",
-                    "fee_amount": "10.00",
-                    "tax_amount": "20.00",
-                    "restricted_ticket": False,
-                    "legs": [
-                        {
-                            "conjunction_ticket": "CJ0001",
-                            "exchange_ticket": "ET0001",
-                            "coupon_number": "1",
-                            "service_class": "Y",
-                            "carrier_code": "AA",
-                            "fare_basis_code": "W",
-                            "flight_number": "AA100",
-                            "departure_date": date(2018, 1, 2),
-                            "departure_airport_code": "MDW",
-                            "departure_time": "08:00",
-                            "arrival_airport_code": "ATX",
-                            "arrival_time": "10:00",
-                            "stopover_permitted": False,
-                            "fare_amount": "35.00",
-                            "fee_amount": "5.00",
-                            "tax_amount": "10.00",
-                            "endorsement_or_restrictions": "NOT REFUNDABLE"
-                        },
-                        {
-                            "conjunction_ticket": "CJ0002",
-                            "exchange_ticket": "ET0002",
-                            "coupon_number": "1",
-                            "service_class": "Y",
-                            "carrier_code": "AA",
-                            "fare_basis_code": "W",
-                            "flight_number": "AA200",
-                            "departure_date": date(2018, 1, 3),
-                            "departure_airport_code": "ATX",
-                            "departure_time": "12:00",
-                            "arrival_airport_code": "MDW",
-                            "arrival_time": "14:00",
-                            "stopover_permitted": False,
-                            "fare_amount": "35.00",
-                            "fee_amount": "5.00",
-                            "tax_amount": "10.00",
-                            "endorsement_or_restrictions": "NOT REFUNDABLE"
-                        }
-                    ]
-                }
-            }
-        })
-
-        self.assertTrue(result.is_success)
-
-    def test_transactions_return_validation_errors_on_travel_flight_industry_data(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "payment_method_nonce": Nonces.PayPalOneTimePayment,
-            "options": {"submit_for_settlement": True},
-            "industry": {
-                "industry_type": Transaction.IndustryType.TravelAndFlight,
-                "data": {
-                    "fare_amount": "-1.23",
-                    "legs": [
-                        {
-                            "fare_amount": "-1.23"
-                        }
-                    ]
-                }
-            }
-        })
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(
-            ErrorCodes.Transaction.Industry.TravelFlight.FareAmountCannotBeNegative,
-            result.errors.for_object("transaction").for_object("industry").on("fare_amount")[0].code
-        )
-        self.assertEqual(
-            ErrorCodes.Transaction.Industry.Leg.TravelFlight.FareAmountCannotBeNegative,
-            result.errors.for_object("transaction").for_object("industry").for_object("legs").for_object("index_0").on("fare_amount")[0].code
-        )
-
-    def test_descriptors_accepts_name_phone_and_url(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            },
-            "descriptor": {
-                "name": "123*123456789012345678",
-                "phone": "3334445555",
-                "url": "ebay.com"
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-        self.assertEqual("123*123456789012345678", transaction.descriptor.name)
-        self.assertEqual("3334445555", transaction.descriptor.phone)
-        self.assertEqual("ebay.com", transaction.descriptor.url)
-
-    def test_descriptors_has_validation_errors_if_format_is_invalid(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            },
-            "descriptor": {
-                "name": "badcompanyname12*badproduct12",
-                "phone": "%bad4445555",
-                "url": "12345678901234"
-            }
-        })
-        self.assertFalse(result.is_success)
-        self.assertEqual(
-            ErrorCodes.Descriptor.NameFormatIsInvalid,
-            result.errors.for_object("transaction").for_object("descriptor").on("name")[0].code
-        )
-        self.assertEqual(
-            ErrorCodes.Descriptor.PhoneFormatIsInvalid,
-            result.errors.for_object("transaction").for_object("descriptor").on("phone")[0].code
-        )
-        self.assertEqual(
-            ErrorCodes.Descriptor.UrlFormatIsInvalid,
-            result.errors.for_object("transaction").for_object("descriptor").on("url")[0].code
-        )
-
-    def test_clone_transaction(self):
-        result = Transaction.sale({
-            "amount": "100.00",
-            "order_id": "123",
-            "credit_card": {
-                "number": "5105105105105100",
-                "expiration_date": "05/2011",
-            },
-            "customer": {
-                "first_name": "Dan",
-            },
-            "billing": {
-                "first_name": "Carl",
-            },
-            "shipping": {
-                "first_name": "Andrew",
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-
-        clone_result = Transaction.clone_transaction(
-                transaction.id,
-                {
-                    "amount": "123.45",
-                    "channel": "MyShoppingCartProvider",
-                    "options": {"submit_for_settlement": "false"}
-                })
-        self.assertTrue(clone_result.is_success)
-        clone_transaction = clone_result.transaction
-
-        self.assertNotEqual(transaction.id, clone_transaction.id)
-
-        self.assertEqual(Transaction.Type.Sale, clone_transaction.type)
-        self.assertEqual(Transaction.Status.Authorized, clone_transaction.status)
-        self.assertEqual(Decimal("123.45"), clone_transaction.amount)
-        self.assertEqual("MyShoppingCartProvider", clone_transaction.channel)
-        self.assertEqual("123", clone_transaction.order_id)
-        self.assertEqual("510510******5100", clone_transaction.credit_card_details.masked_number)
-        self.assertEqual("Dan", clone_transaction.customer_details.first_name)
-        self.assertEqual("Carl", clone_transaction.billing_details.first_name)
-        self.assertEqual("Andrew", clone_transaction.shipping_details.first_name)
-
-    def test_clone_transaction_submits_for_settlement(self):
-        result = Transaction.sale({
-            "amount": "100.00",
-            "credit_card": {
-                "number": "5105105105105100",
-                "expiration_date": "05/2011",
-            }
-        })
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-
-        clone_result = Transaction.clone_transaction(transaction.id, {"amount": "123.45", "options": {"submit_for_settlement": "true"}})
-        self.assertTrue(clone_result.is_success)
-        clone_transaction = clone_result.transaction
-
-        self.assertEqual(Transaction.Type.Sale, clone_transaction.type)
-        self.assertEqual(Transaction.Status.SubmittedForSettlement, clone_transaction.status)
-
-    def test_clone_transaction_with_validations(self):
-        result = Transaction.credit({
-            "amount": "100.00",
-            "credit_card": {
-                "number": "5105105105105100",
-                "expiration_date": "05/2011",
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-
-        clone_result = Transaction.clone_transaction(transaction.id, {"amount": "123.45"})
-        self.assertFalse(clone_result.is_success)
-
-        self.assertEqual(
-            ErrorCodes.Transaction.CannotCloneCredit,
-            clone_result.errors.for_object("transaction").on("base")[0].code
-        )
-
-    def test_find_exposes_disbursement_details(self):
-        transaction = Transaction.find("deposittransaction")
-        disbursement_details = transaction.disbursement_details
-
-        self.assertEqual(date(2013, 4, 10), disbursement_details.disbursement_date)
-        self.assertEqual("USD", disbursement_details.settlement_currency_iso_code)
-        self.assertEqual(Decimal("1"), disbursement_details.settlement_currency_exchange_rate)
-        self.assertEqual(False, disbursement_details.funds_held)
-        self.assertEqual(True, disbursement_details.success)
-        self.assertEqual(Decimal("100.00"), disbursement_details.settlement_amount)
-
-    def test_sale_with_three_d_secure_option(self):
-        result = Transaction.sale({
-            "merchant_account_id": TestHelper.three_d_secure_merchant_account_id,
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            },
-            "options": {
-                "three_d_secure": {
-                    "required": True
-                }
-            }
-        })
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(Transaction.Status.GatewayRejected, result.transaction.status)
-        self.assertEqual(Transaction.GatewayRejectionReason.ThreeDSecure, result.transaction.gateway_rejection_reason)
-
-    def test_sale_with_three_d_secure_token(self):
-        three_d_secure_token = TestHelper.create_3ds_verification(TestHelper.three_d_secure_merchant_account_id, {
-            "number": "4111111111111111",
-            "expiration_month": "05",
-            "expiration_year": "2009",
-        })
-
-        result = Transaction.sale({
-            "merchant_account_id": TestHelper.three_d_secure_merchant_account_id,
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            },
-            "three_d_secure_token": three_d_secure_token
-        })
-
-        self.assertTrue(result.is_success)
-
-    def test_sale_without_three_d_secure_token(self):
-        result = Transaction.sale({
-            "merchant_account_id": TestHelper.three_d_secure_merchant_account_id,
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            }
-        })
-
-        self.assertTrue(result.is_success)
-
-    def test_sale_returns_error_with_none_three_d_secure_token(self):
-        result = Transaction.sale({
-            "merchant_account_id": TestHelper.three_d_secure_merchant_account_id,
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            },
-            "three_d_secure_token": None
-        })
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(
-            ErrorCodes.Transaction.ThreeDSecureTokenIsInvalid,
-            result.errors.for_object("transaction").on("three_d_secure_token")[0].code
-        )
-
-    def test_sale_returns_error_with_mismatched_3ds_verification_data(self):
-        three_d_secure_token = TestHelper.create_3ds_verification(TestHelper.three_d_secure_merchant_account_id, {
-            "number": "4111111111111111",
-            "expiration_month": "05",
-            "expiration_year": "2009",
-        })
-
-        result = Transaction.sale({
-            "merchant_account_id": TestHelper.three_d_secure_merchant_account_id,
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "5105105105105100",
-                "expiration_date": "05/2009"
-            },
-            "three_d_secure_token": three_d_secure_token
-        })
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(
-            ErrorCodes.Transaction.ThreeDSecureTransactionDataDoesntMatchVerify,
-            result.errors.for_object("transaction").on("three_d_secure_token")[0].code
-        )
-
-    def test_transaction_with_three_d_secure_pass_thru(self):
-        result = Transaction.sale({
-            "merchant_account_id": TestHelper.three_d_secure_merchant_account_id,
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            },
-            "three_d_secure_pass_thru": {
-                "eci_flag": "02",
-                "cavv": "some-cavv",
-                "xid": "some-xid"
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertEqual(Transaction.Status.Authorized, result.transaction.status)
-
-    def test_transaction_with_three_d_secure_pass_thru_with_invalid_processor_settings(self):
-        result = Transaction.sale({
-            "merchant_account_id": "heartland_ma",
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            },
-            "three_d_secure_pass_thru": {
-                "eci_flag": "02",
-                "cavv": "some-cavv",
-                "xid": "some-xid"
-            }
-        })
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(
-            ErrorCodes.Transaction.ThreeDSecureMerchantAccountDoesNotSupportCardType,
-            result.errors.for_object("transaction").on("merchant_account_id")[0].code
-        )
-
-    def test_transaction_with_three_d_secure_pass_thru_with_missing_eci_flag(self):
-        result = Transaction.sale({
-            "merchant_account_id": TestHelper.three_d_secure_merchant_account_id,
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            },
-            "three_d_secure_pass_thru": {
-                "eci_flag": "",
-                "cavv": "some-cavv",
-                "xid": "some-xid"
-            }
-        })
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(
-            ErrorCodes.Transaction.ThreeDSecureEciFlagIsRequired,
-            result.errors.for_object("transaction").for_object("three_d_secure_pass_thru").on("eci_flag")[0].code
-        )
-
-
-    def test_transaction_with_three_d_secure_pass_thru_with_missing_cavv_and_xid(self):
-        result = Transaction.sale({
-            "merchant_account_id": TestHelper.three_d_secure_merchant_account_id,
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            },
-            "three_d_secure_pass_thru": {
-                "eci_flag": "05",
-                "cavv": "",
-                "xid": ""
-            }
-        })
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(
-            ErrorCodes.Transaction.ThreeDSecureCavvIsRequired,
-            result.errors.for_object("transaction").for_object("three_d_secure_pass_thru").on("cavv")[0].code
-        )
-
-    def test_transaction_with_three_d_secure_pass_thru_with_invalid_eci_flag(self):
-        result = Transaction.sale({
-            "merchant_account_id": TestHelper.three_d_secure_merchant_account_id,
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            },
-            "three_d_secure_pass_thru": {
-                "eci_flag": "bad_eci_flag",
-                "cavv": "some-cavv",
-                "xid": "some-xid"
-            }
-        })
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(
-            ErrorCodes.Transaction.ThreeDSecureEciFlagIsInvalid,
-            result.errors.for_object("transaction").for_object("three_d_secure_pass_thru").on("eci_flag")[0].code
-        )
-
-    def test_transaction_with_three_d_secure_adyen_pass_thru(self):
-        result = Transaction.sale({
-            "merchant_account_id": TestHelper.adyen_merchant_account_id,
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "10/2020",
-                "cvv": "737"
-            },
-            "three_d_secure_pass_thru": {
-                "eci_flag": "02",
-                "cavv": "some-cavv",
-                "xid": "some-xid",
-                "authentication_response": "Y",
-                "directory_response": "Y",
-                "cavv_algorithm": "2",
-                "ds_transaction_id": "dstrxid-present",
-                "three_d_secure_version": "1.0.2",
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertEqual(Transaction.Status.Authorized, result.transaction.status)
-
-    def test_transaction_with_three_d_secure_adyen_pass_thru_missing_authentication_response(self):
-        result = Transaction.sale({
-            "merchant_account_id": TestHelper.adyen_merchant_account_id,
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "10/2020",
-                "cvv": "737"
-            },
-            "three_d_secure_pass_thru": {
-                "eci_flag": "02",
-                "cavv": "some-cavv",
-                "xid": "some-xid",
-                "authentication_response": "",
-                "directory_response": "Y",
-                "cavv_algorithm": "2",
-                "ds_transaction_id": "dstrxid-present",
-                "three_d_secure_version": "1.0.2",
-            }
-        })
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(
-            ErrorCodes.Transaction.ThreeDSecureAuthenticationResponseIsInvalid,
-            result.errors.for_object("transaction").for_object("three_d_secure_pass_thru").on("authentication_response")[0].code
-        )
-
-    def test_transaction_with_three_d_secure_adyen_pass_thru_missing_directory_response(self):
-        result = Transaction.sale({
-            "merchant_account_id": TestHelper.adyen_merchant_account_id,
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "10/2020",
-                "cvv": "737"
-            },
-            "three_d_secure_pass_thru": {
-                "eci_flag": "02",
-                "cavv": "some-cavv",
-                "xid": "some-xid",
-                "authentication_response": "Y",
-                "directory_response": "",
-                "cavv_algorithm": "2",
-                "ds_transaction_id": "dstrxid-present",
-                "three_d_secure_version": "1.0.2",
-            }
-        })
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(
-            ErrorCodes.Transaction.ThreeDSecureDirectoryResponseIsInvalid,
-            result.errors.for_object("transaction").for_object("three_d_secure_pass_thru").on("directory_response")[0].code
-        )
-
-    def test_transaction_with_three_d_secure_adyen_pass_thru_missing_cavv_algorithm(self):
-        result = Transaction.sale({
-            "merchant_account_id": TestHelper.adyen_merchant_account_id,
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "10/2020",
-                "cvv": "737"
-            },
-            "three_d_secure_pass_thru": {
-                "eci_flag": "02",
-                "cavv": "some-cavv",
-                "xid": "some-xid",
-                "authentication_response": "Y",
-                "directory_response": "Y",
-                "cavv_algorithm": "",
-                "ds_transaction_id": "dstrxid-present",
-                "three_d_secure_version": "1.0.2",
-            }
-        })
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(
-            ErrorCodes.Transaction.ThreeDSecureCavvAlgorithmIsInvalid,
-            result.errors.for_object("transaction").for_object("three_d_secure_pass_thru").on("cavv_algorithm")[0].code
-        )
-
-    def test_sale_with_amex_rewards_succeeds(self):
-        result = Transaction.sale({
-            "merchant_account_id": TestHelper.fake_amex_direct_merchant_account_id,
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": CreditCardNumbers.AmexPayWithPoints.Success,
-                "expiration_date": "05/2020"
-            },
-            "options" : {
-                "submit_for_settlement" : True,
-                "amex_rewards" : {
-                    "request_id" : "ABC123",
-                    "points" : "100",
-                    "currency_amount" : "1.00",
-                    "currency_iso_code" : "USD"
-                }
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-        self.assertEqual(Transaction.Type.Sale, transaction.type)
-        self.assertEqual(Transaction.Status.SubmittedForSettlement, transaction.status)
-
-    def test_sale_with_amex_rewards_succeeds_even_if_card_is_ineligible(self):
-        result = Transaction.sale({
-            "merchant_account_id": TestHelper.fake_amex_direct_merchant_account_id,
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": CreditCardNumbers.AmexPayWithPoints.IneligibleCard,
-                "expiration_date": "05/2009"
-            },
-            "options" : {
-                "submit_for_settlement" : True,
-                "amex_rewards" : {
-                    "request_id" : "ABC123",
-                    "points" : "100",
-                    "currency_amount" : "1.00",
-                    "currency_iso_code" : "USD"
-                }
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-        self.assertEqual(Transaction.Type.Sale, transaction.type)
-        self.assertEqual(Transaction.Status.SubmittedForSettlement, transaction.status)
-
-    def test_sale_with_amex_rewards_succeeds_even_if_card_balance_is_insufficient(self):
-        result = Transaction.sale({
-            "merchant_account_id": TestHelper.fake_amex_direct_merchant_account_id,
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": CreditCardNumbers.AmexPayWithPoints.InsufficientPoints,
-                "expiration_date": "05/2009"
-            },
-            "options" : {
-                "submit_for_settlement" : True,
-                "amex_rewards" : {
-                    "request_id" : "ABC123",
-                    "points" : "100",
-                    "currency_amount" : "1.00",
-                    "currency_iso_code" : "USD"
-                }
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-        self.assertEqual(Transaction.Type.Sale, transaction.type)
-        self.assertEqual(Transaction.Status.SubmittedForSettlement, transaction.status)
-
-    def test_submit_for_settlement_with_amex_rewards_succeeds(self):
-        result = Transaction.sale({
-            "merchant_account_id": TestHelper.fake_amex_direct_merchant_account_id,
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": CreditCardNumbers.AmexPayWithPoints.Success,
-                "expiration_date": "05/2009"
-            },
-            "options" : {
-                "amex_rewards" : {
-                    "request_id" : "ABC123",
-                    "points" : "100",
-                    "currency_amount" : "1.00",
-                    "currency_iso_code" : "USD"
-                }
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-        self.assertEqual(Transaction.Type.Sale, transaction.type)
-        self.assertEqual(Transaction.Status.Authorized, transaction.status)
-
-        submitted_transaction = Transaction.submit_for_settlement(transaction.id).transaction
-        self.assertEqual(Transaction.Status.SubmittedForSettlement, submitted_transaction.status)
-
-    def test_submit_for_settlement_with_amex_rewards_succeeds_even_if_card_is_ineligible(self):
-        result = Transaction.sale({
-            "merchant_account_id": TestHelper.fake_amex_direct_merchant_account_id,
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": CreditCardNumbers.AmexPayWithPoints.IneligibleCard,
-                "expiration_date": "05/2009"
-            },
-            "options" : {
-                "amex_rewards" : {
-                    "request_id" : "ABC123",
-                    "points" : "100",
-                    "currency_amount" : "1.00",
-                    "currency_iso_code" : "USD"
-                }
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-        self.assertEqual(Transaction.Type.Sale, transaction.type)
-        self.assertEqual(Transaction.Status.Authorized, transaction.status)
-
-        submitted_transaction = Transaction.submit_for_settlement(transaction.id).transaction
-        self.assertEqual(Transaction.Status.SubmittedForSettlement, submitted_transaction.status)
-
-    def test_submit_for_settlement_with_amex_rewards_succeeds_even_if_card_balance_is_insufficient(self):
-        result = Transaction.sale({
-            "merchant_account_id": TestHelper.fake_amex_direct_merchant_account_id,
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": CreditCardNumbers.AmexPayWithPoints.InsufficientPoints,
-                "expiration_date": "05/2009"
-            },
-            "options" : {
-                "amex_rewards" : {
-                    "request_id" : "ABC123",
-                    "points" : "100",
-                    "currency_amount" : "1.00",
-                    "currency_iso_code" : "USD"
-                }
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-        self.assertEqual(Transaction.Type.Sale, transaction.type)
-        self.assertEqual(Transaction.Status.Authorized, transaction.status)
-
-        submitted_transaction = Transaction.submit_for_settlement(transaction.id).transaction
-        self.assertEqual(Transaction.Status.SubmittedForSettlement, submitted_transaction.status)
-
-    def test_find_exposes_authorization_adjustments(self):
-        transaction = Transaction.find("authadjustmenttransaction")
-        authorization_adjustment = transaction.authorization_adjustments[0]
-
-        self.assertEqual(datetime, type(authorization_adjustment.timestamp))
-        self.assertEqual(Decimal("-20.00"), authorization_adjustment.amount)
-        self.assertEqual(True, authorization_adjustment.success)
-        self.assertEqual("1000", authorization_adjustment.processor_response_code)
-        self.assertEqual("Approved", authorization_adjustment.processor_response_text)
-        self.assertEqual(ProcessorResponseTypes.Approved, authorization_adjustment.processor_response_type)
-
-    def test_find_exposes_authorization_adjustments_soft_declined(self):
-        transaction = Transaction.find("authadjustmenttransactionsoftdeclined")
-        authorization_adjustment = transaction.authorization_adjustments[0]
-
-        self.assertEqual(datetime, type(authorization_adjustment.timestamp))
-        self.assertEqual(Decimal("-20.00"), authorization_adjustment.amount)
-        self.assertEqual(False, authorization_adjustment.success)
-        self.assertEqual("3000", authorization_adjustment.processor_response_code)
-        self.assertEqual("Processor Network Unavailable - Try Again", authorization_adjustment.processor_response_text)
-        self.assertEqual(ProcessorResponseTypes.SoftDeclined, authorization_adjustment.processor_response_type)
-
-    def test_find_exposes_authorization_adjustments_hard_declined(self):
-        transaction = Transaction.find("authadjustmenttransactionharddeclined")
-        authorization_adjustment = transaction.authorization_adjustments[0]
-
-        self.assertEqual(datetime, type(authorization_adjustment.timestamp))
-        self.assertEqual(Decimal("-20.00"), authorization_adjustment.amount)
-        self.assertEqual(False, authorization_adjustment.success)
-        self.assertEqual("2015", authorization_adjustment.processor_response_code)
-        self.assertEqual("Transaction Not Allowed", authorization_adjustment.processor_response_text)
-        self.assertEqual(ProcessorResponseTypes.HardDeclined, authorization_adjustment.processor_response_type)
-
-    def test_find_exposes_disputes(self):
-        transaction = Transaction.find("disputedtransaction")
-        dispute = transaction.disputes[0]
-
-        self.assertEqual(date(2014, 3, 1), dispute.received_date)
-        self.assertEqual(date(2014, 3, 21), dispute.reply_by_date)
-        self.assertEqual("USD", dispute.currency_iso_code)
-        self.assertEqual(Decimal("250.00"), dispute.amount)
-        self.assertEqual(Dispute.Status.Won, dispute.status)
-        self.assertEqual(Dispute.Reason.Fraud, dispute.reason)
-        self.assertEqual("disputedtransaction", dispute.transaction_details.id)
-        self.assertEqual(Decimal("1000.00"), dispute.transaction_details.amount)
-        self.assertEqual(Dispute.Kind.Chargeback, dispute.kind)
-        self.assertEqual(date(2014, 3, 1), dispute.date_opened)
-        self.assertEqual(date(2014, 3, 7), dispute.date_won)
-
-    def test_find_exposes_three_d_secure_info(self):
-        transaction = Transaction.find("threedsecuredtransaction")
-        three_d_secure_info = transaction.three_d_secure_info
-
-        self.assertEqual("Y", three_d_secure_info.enrolled)
-        self.assertEqual("authenticate_successful", three_d_secure_info.status)
-        self.assertEqual(True, three_d_secure_info.liability_shifted)
-        self.assertEqual(True, three_d_secure_info.liability_shift_possible)
-        self.assertEqual("somebase64value", three_d_secure_info.cavv)
-        self.assertEqual("xidvalue", three_d_secure_info.xid)
-        self.assertEqual("dstxnid", three_d_secure_info.ds_transaction_id)
-        self.assertEqual("07", three_d_secure_info.eci_flag)
-        self.assertEqual("1.0.2", three_d_secure_info.three_d_secure_version)
-
-    def test_find_exposes_none_for_null_three_d_secure_info(self):
-        transaction = Transaction.find("settledtransaction")
-        three_d_secure_info = transaction.three_d_secure_info
-
-        self.assertEqual(None, three_d_secure_info)
-
-    def test_find_exposes_refund_from_transaction_fee(self):
-        transaction = Transaction.find("settledtransaction")
-        paypal_details = transaction.paypal_details
-
-        self.assertNotEqual(None, paypal_details.refund_from_transaction_fee_amount)
-        self.assertNotEqual(None, paypal_details.refund_from_transaction_fee_currency_iso_code)
-
-    def test_find_exposes_retrievals(self):
-        transaction = Transaction.find("retrievaltransaction")
-        dispute = transaction.disputes[0]
-
-        self.assertEqual("USD", dispute.currency_iso_code)
-        self.assertEqual(Decimal("1000.00"), dispute.amount)
-        self.assertEqual(Dispute.Status.Open, dispute.status)
-        self.assertEqual(Dispute.Reason.Retrieval, dispute.reason)
-        self.assertEqual("retrievaltransaction", dispute.transaction_details.id)
-        self.assertEqual(Decimal("1000.00"), dispute.transaction_details.amount)
-
-    def test_creating_paypal_transaction_with_one_time_use_nonce(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "payment_method_nonce": Nonces.PayPalOneTimePayment,
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-
-        self.assertEqual(transaction.paypal_details.payer_email, "payer@example.com")
-        self.assertNotEqual(None, re.search(r'PAY-\w+', transaction.paypal_details.payment_id))
-        self.assertNotEqual(None, re.search(r'AUTH-\w+', transaction.paypal_details.authorization_id))
-        self.assertNotEqual(None, transaction.paypal_details.image_url)
-        self.assertNotEqual(None, transaction.paypal_details.debug_id)
-
-    def test_creating_local_payment_transaction_with_local_payment_nonce(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "options": {
-                "submit_for_settlement": True,
-            },
-            "payment_method_nonce": Nonces.LocalPayment,
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-
-        self.assertNotEqual(None, transaction.local_payment_details.payment_id)
-        self.assertNotEqual(None, transaction.local_payment_details.payer_id)
-        self.assertNotEqual(None, transaction.local_payment_details.funding_source)
-        self.assertNotEqual(None, transaction.local_payment_details.capture_id)
-        self.assertNotEqual(None, transaction.local_payment_details.debug_id)
-        self.assertNotEqual(None, transaction.local_payment_details.transaction_fee_amount)
-        self.assertNotEqual(None, transaction.local_payment_details.transaction_fee_currency_iso_code)
-
-    def test_refunding_local_payment_transaction(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "options": {
-                "submit_for_settlement": True,
-            },
-            "payment_method_nonce": Nonces.LocalPayment,
-        })
-
-        self.assertTrue(result.is_success)
-
-        result = Transaction.refund(result.transaction.id)
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-
-        self.assertNotEqual(None, transaction.local_payment_details.payment_id)
-        self.assertNotEqual(None, transaction.local_payment_details.payer_id)
-        self.assertNotEqual(None, transaction.local_payment_details.funding_source)
-        self.assertNotEqual(None, transaction.local_payment_details.refund_id)
-        self.assertNotEqual(None, transaction.local_payment_details.debug_id)
-        self.assertNotEqual(None, transaction.local_payment_details.refund_from_transaction_fee_amount)
-        self.assertNotEqual(None, transaction.local_payment_details.refund_from_transaction_fee_currency_iso_code)
-
-    def test_creating_local_payment_transaction_with_local_payment_webhook_content(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "options": {
-                "submit_for_settlement": True,
-            },
-            "paypal_account": {
-                "payment_id": "PAY-1234",
-                "payer_id": "PAYER-1234",
-            },
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-
-        self.assertEqual(transaction.paypal_details.payment_id, "PAY-1234")
-        self.assertEqual(transaction.paypal_details.payer_id, "PAYER-1234")
-
-    def test_creating_paypal_transaction_with_payee_id(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "payment_method_nonce": Nonces.PayPalOneTimePayment,
-            "paypal_account": {
-                "payee_id": "fake-payee-id"
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-
-        self.assertEqual(transaction.paypal_details.payer_email, "payer@example.com")
-        self.assertNotEqual(None, re.search(r'PAY-\w+', transaction.paypal_details.payment_id))
-        self.assertNotEqual(None, re.search(r'AUTH-\w+', transaction.paypal_details.authorization_id))
-        self.assertNotEqual(None, transaction.paypal_details.image_url)
-        self.assertNotEqual(None, transaction.paypal_details.debug_id)
-        self.assertEqual(transaction.paypal_details.payee_id, "fake-payee-id")
-
-    def test_creating_paypal_transaction_with_payee_email(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "payment_method_nonce": Nonces.PayPalOneTimePayment,
-            "paypal_account": {
-                "payee_email": "payee@example.com"
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-
-        self.assertEqual(transaction.paypal_details.payer_email, "payer@example.com")
-        self.assertNotEqual(None, re.search(r'PAY-\w+', transaction.paypal_details.payment_id))
-        self.assertNotEqual(None, re.search(r'AUTH-\w+', transaction.paypal_details.authorization_id))
-        self.assertNotEqual(None, transaction.paypal_details.image_url)
-        self.assertNotEqual(None, transaction.paypal_details.debug_id)
-        self.assertEqual(transaction.paypal_details.payee_email, "payee@example.com")
-
-    def test_creating_paypal_transaction_with_payee_email_in_options_params(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "payment_method_nonce": Nonces.PayPalOneTimePayment,
-            "paypal_account": {},
-            "options": {
-                "payee_email": "payee@example.com"
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-
-        self.assertEqual(transaction.paypal_details.payer_email, "payer@example.com")
-        self.assertNotEqual(None, re.search(r'PAY-\w+', transaction.paypal_details.payment_id))
-        self.assertNotEqual(None, re.search(r'AUTH-\w+', transaction.paypal_details.authorization_id))
-        self.assertNotEqual(None, transaction.paypal_details.image_url)
-        self.assertNotEqual(None, transaction.paypal_details.debug_id)
-        self.assertEqual(transaction.paypal_details.payee_email, "payee@example.com")
-
-    def test_creating_paypal_transaction_with_payee_email_in_options_paypal_params(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "payment_method_nonce": Nonces.PayPalOneTimePayment,
-            "paypal_account": {},
-            "options": {
-                "paypal": {
-                    "payee_email": "foo@paypal.com"
-                }
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-
-        self.assertEqual(transaction.paypal_details.payer_email, "payer@example.com")
-        self.assertNotEqual(None, re.search(r'PAY-\w+', transaction.paypal_details.payment_id))
-        self.assertNotEqual(None, re.search(r'AUTH-\w+', transaction.paypal_details.authorization_id))
-        self.assertNotEqual(None, transaction.paypal_details.image_url)
-        self.assertNotEqual(None, transaction.paypal_details.debug_id)
-        self.assertEqual(transaction.paypal_details.payee_email, "foo@paypal.com")
-
-    def test_creating_paypal_transaction_with_custom_field_in_options_paypal_params(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "payment_method_nonce": Nonces.PayPalOneTimePayment,
-            "paypal_account": {},
-            "options": {
-                "paypal": {
-                    "custom_field": "custom field stuff"
-                }
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-
-        self.assertEqual(transaction.paypal_details.payer_email, "payer@example.com")
-        self.assertNotEqual(None, re.search(r'PAY-\w+', transaction.paypal_details.payment_id))
-        self.assertNotEqual(None, re.search(r'AUTH-\w+', transaction.paypal_details.authorization_id))
-        self.assertNotEqual(None, transaction.paypal_details.image_url)
-        self.assertNotEqual(None, transaction.paypal_details.debug_id)
-        self.assertEqual(transaction.paypal_details.custom_field, "custom field stuff")
-
-    def test_creating_paypal_transaction_with_supplementary_data_in_options_paypal_params(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "payment_method_nonce": Nonces.PayPalOneTimePayment,
-            "paypal_account": {},
-            "options": {
-                "paypal": {
-                    "supplementary_data": {
-                        "key1": "value1",
-                        "key2": "value2"
-                    }
-                }
-            }
-        })
-
-        # note - supplementary data is not returned in response
-        self.assertTrue(result.is_success)
-
-    def test_creating_paypal_transaction_with_description_in_options_paypal_params(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "payment_method_nonce": Nonces.PayPalOneTimePayment,
-            "paypal_account": {},
-            "options": {
-                "paypal": {
-                    "description": "Product description"
-                }
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-
-        self.assertEqual(transaction.paypal_details.payer_email, "payer@example.com")
-        self.assertNotEqual(None, re.search(r'PAY-\w+', transaction.paypal_details.payment_id))
-        self.assertNotEqual(None, re.search(r'AUTH-\w+', transaction.paypal_details.authorization_id))
-        self.assertNotEqual(None, transaction.paypal_details.image_url)
-        self.assertNotEqual(None, transaction.paypal_details.debug_id)
-        self.assertEqual(transaction.paypal_details.description, "Product description")
-
-    def test_paypal_transaction_payment_instrument_type(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "payment_method_nonce": Nonces.PayPalOneTimePayment,
-        })
-
-        self.assertTrue(result.is_success)
-
-        transaction = result.transaction
-        self.assertEqual(PaymentInstrumentType.PayPalAccount, transaction.payment_instrument_type)
-
-    def test_creating_paypal_transaction_with_one_time_use_nonce_and_store_in_vault(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "payment_method_nonce": Nonces.PayPalOneTimePayment,
-            "options": {"store_in_vault": True}
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-
-        self.assertEqual(transaction.paypal_details.payer_email, "payer@example.com")
-        self.assertEqual(transaction.paypal_details.token, None)
-        self.assertNotEqual(None, transaction.paypal_details.debug_id)
-
-    def test_creating_paypal_transaction_with_future_payment_nonce(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "payment_method_nonce": Nonces.PayPalFuturePayment
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-
-        self.assertEqual(transaction.paypal_details.payer_email, "payer@example.com")
-        self.assertNotEqual(None, re.search(r'PAY-\w+', transaction.paypal_details.payment_id))
-        self.assertNotEqual(None, re.search(r'AUTH-\w+', transaction.paypal_details.authorization_id))
-        self.assertNotEqual(None, transaction.paypal_details.debug_id)
-
-    def test_validation_failure_on_invalid_paypal_nonce(self):
-        http = ClientApiHttp.create()
-
-        status_code, nonce = http.get_paypal_nonce({
-            "consent-code": "consent-code",
-            "access-token": "access-token",
-            "options": {"validate": False}
-        })
-        self.assertEqual(202, status_code)
-
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "payment_method_nonce": nonce
-        })
-
-        self.assertFalse(result.is_success)
-        error_code = result.errors.for_object("transaction").for_object("paypal_account").on("base")[0].code
-        self.assertEqual(error_code, ErrorCodes.PayPalAccount.CannotHaveBothAccessTokenAndConsentCode)
-
-    def test_validation_failure_on_non_existent_nonce(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "payment_method_nonce": "doesnt-exist"
-        })
-
-        self.assertFalse(result.is_success)
-        error_code = result.errors.for_object("transaction").on("payment_method_nonce")[0].code
-        self.assertEqual(error_code, ErrorCodes.Transaction.PaymentMethodNonceUnknown)
-
-    def test_creating_paypal_transaction_with_vaulted_token(self):
-        customer_id = Customer.create().customer.id
-
-        result = PaymentMethod.create({
-            "customer_id": customer_id,
-            "payment_method_nonce": Nonces.PayPalFuturePayment
-        })
-
-        self.assertTrue(result.is_success)
-
-        transaction_result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "payment_method_token": result.payment_method.token
-        })
-
-        self.assertTrue(transaction_result.is_success)
-        transaction = transaction_result.transaction
-
-        self.assertEqual(transaction.paypal_details.payer_email, "payer@example.com")
-        self.assertNotEqual(None, transaction.paypal_details.debug_id)
-
-    def test_creating_paypal_transaction_with_one_time_nonce_and_store_in_vault_fails_gracefully(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "payment_method_nonce": Nonces.PayPalOneTimePayment,
-            "options": {"store_in_vault": True}
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-        self.assertEqual(None, transaction.paypal_details.token)
-
-    def test_creating_paypal_transaction_with_future_payment_nonce_and_store_in_vault(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "payment_method_nonce": Nonces.PayPalFuturePayment,
-            "options": {"store_in_vault": True}
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-
-        self.assertNotEqual(None, transaction.paypal_details.token)
-        paypal_account = PaymentMethod.find(transaction.paypal_details.token)
-        self.assertEqual(paypal_account.email, transaction.paypal_details.payer_email)
-
-    def test_creating_paypal_transaction_and_submitting_for_settlement(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "payment_method_nonce": Nonces.PayPalOneTimePayment,
-            "options": {"submit_for_settlement": True}
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertEqual(result.transaction.status, Transaction.Status.Settling)
-
-    def test_voiding_a_paypal_transaction(self):
-        sale_result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "payment_method_nonce": Nonces.PayPalOneTimePayment,
-        })
-        self.assertTrue(sale_result.is_success)
-        sale_transaction = sale_result.transaction
-
-        void_result = Transaction.void(sale_transaction.id)
-        self.assertTrue(void_result.is_success)
-
-        void_transaction = void_result.transaction
-        self.assertEqual(void_transaction.id, sale_transaction.id)
-        self.assertEqual(void_transaction.status, Transaction.Status.Voided)
-
-    def test_paypal_transaction_successful_refund(self):
-        transaction = self.__create_paypal_transaction()
-
-        result = Transaction.refund(transaction.id)
-
-        self.assertTrue(result.is_success)
-        refund = result.transaction
-
-        self.assertEqual(Transaction.Type.Credit, refund.type)
-        self.assertEqual(Decimal(TransactionAmounts.Authorize), refund.amount)
-        self.assertEqual(transaction.id, refund.refunded_transaction_id)
-
-        self.assertEqual(refund.id, Transaction.find(transaction.id).refund_id)
-
-    def test_paypal_transaction_successful_partial_refund(self):
-        transaction = self.__create_paypal_transaction()
-
-        result = Transaction.refund(transaction.id, Decimal("500.00"))
-
-        self.assertTrue(result.is_success)
-        self.assertEqual(Transaction.Type.Credit, result.transaction.type)
-        self.assertEqual(Decimal("500.00"), result.transaction.amount)
-
-    def test_paypal_transaction_multiple_successful_partial_refunds(self):
-        transaction = self.__create_paypal_transaction()
-
-        refund1 = Transaction.refund(transaction.id, Decimal("500.00")).transaction
-        self.assertEqual(Transaction.Type.Credit, refund1.type)
-        self.assertEqual(Decimal("500.00"), refund1.amount)
-
-        refund2 = Transaction.refund(transaction.id, Decimal("500.00")).transaction
-        self.assertEqual(Transaction.Type.Credit, refund2.type)
-        self.assertEqual(Decimal("500.00"), refund2.amount)
-
-        transaction = Transaction.find(transaction.id)
-
-        self.assertEqual(2, len(transaction.refund_ids))
-        self.assertTrue(TestHelper.in_list(transaction.refund_ids, refund1.id))
-        self.assertTrue(TestHelper.in_list(transaction.refund_ids, refund2.id))
-
-    def test_paypal_transaction_returns_required_fields(self):
-        transaction = self.__create_paypal_transaction()
-
-        self.assertNotEqual(None, transaction.paypal_details.debug_id)
-        self.assertNotEqual(None, transaction.paypal_details.payer_email)
-        self.assertNotEqual(None, transaction.paypal_details.authorization_id)
-        self.assertNotEqual(None, transaction.paypal_details.payer_id)
-        self.assertNotEqual(None, transaction.paypal_details.payer_first_name)
-        self.assertNotEqual(None, transaction.paypal_details.payer_last_name)
-        self.assertNotEqual(None, transaction.paypal_details.payer_status)
-        self.assertNotEqual(None, transaction.paypal_details.seller_protection_status)
-        self.assertNotEqual(None, transaction.paypal_details.capture_id)
-        #self.assertNotEqual(None, transaction.paypal_details.refund_id)
-        self.assertNotEqual(None, transaction.paypal_details.transaction_fee_amount)
-        self.assertNotEqual(None, transaction.paypal_details.transaction_fee_currency_iso_code)
-
-    def test_paypal_transaction_refund_returns_an_error_if_unsettled(self):
-        transaction = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            },
-            "options": {
-                "submit_for_settlement": True
-            }
-        }).transaction
-
-        result = Transaction.refund(transaction.id)
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(
-            ErrorCodes.Transaction.CannotRefundUnlessSettled,
-            result.errors.for_object("transaction").on("base")[0].code
-        )
-
-    def test_transaction_settlement_errors(self):
-        sale_result = Transaction.sale({
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2010",
-                "cvv": "100"
-            },
-            "amount": "100.00",
-        })
-        transaction = sale_result.transaction
-
-        settle_result = TestHelper.settle_transaction(transaction.id)
-        self.assertFalse(settle_result.is_success)
-
-        error_codes = [
-            error.code for error in settle_result.errors.for_object("transaction").on("base")
-        ]
-        self.assertTrue(ErrorCodes.Transaction.CannotSimulateTransactionSettlement in error_codes)
-
-    def test_transaction_returns_settlement_declined_response(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "payment_method_nonce": Nonces.PayPalOneTimePayment,
-            "options": {"submit_for_settlement": True}
-        })
-        self.assertTrue(result.is_success)
-        TestHelper.settlement_decline_transaction(result.transaction.id)
-
-        transaction = Transaction.find(result.transaction.id)
-
-        self.assertTrue("4001", transaction.processor_settlement_response_code)
-        self.assertTrue("Settlement Declined", transaction.processor_settlement_response_text)
-        self.assertTrue(Transaction.Status.SettlementDeclined, transaction.status)
-
-    def test_transaction_returns_settlement_pending_response(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "payment_method_nonce": Nonces.PayPalOneTimePayment,
-            "options": {"submit_for_settlement": True}
-        })
-        self.assertTrue(result.is_success)
-        TestHelper.settlement_pending_transaction(result.transaction.id)
-
-        transaction = Transaction.find(result.transaction.id)
-
-        self.assertTrue("4002", transaction.processor_settlement_response_code)
-        self.assertTrue("Settlement Pending", transaction.processor_settlement_response_text)
-        self.assertTrue(Transaction.Status.SettlementPending, transaction.status)
-
-    def test_transaction_submit_for_partial_settlement(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            }
-        })
-        self.assertTrue(result.is_success)
-        authorized_transaction = result.transaction
-
-        partial_settlement_result = Transaction.submit_for_partial_settlement(authorized_transaction.id, Decimal("500.00"))
-        partial_settlement_transaction = partial_settlement_result.transaction
-        self.assertTrue(partial_settlement_result.is_success)
-        self.assertEqual(partial_settlement_transaction.amount, Decimal("500.00"))
-        self.assertEqual(Transaction.Type.Sale, partial_settlement_transaction.type)
-        self.assertEqual(Transaction.Status.SubmittedForSettlement, partial_settlement_transaction.status)
-        self.assertEqual(authorized_transaction.id, partial_settlement_transaction.authorized_transaction_id)
-
-        partial_settlement_result_2 = Transaction.submit_for_partial_settlement(authorized_transaction.id, Decimal("500.00"))
-        partial_settlement_transaction_2 = partial_settlement_result_2.transaction
-        self.assertTrue(partial_settlement_result_2.is_success)
-        self.assertEqual(partial_settlement_transaction_2.amount, Decimal("500.00"))
-        self.assertEqual(Transaction.Type.Sale, partial_settlement_transaction_2.type)
-        self.assertEqual(Transaction.Status.SubmittedForSettlement, partial_settlement_transaction_2.status)
-        self.assertEqual(authorized_transaction.id, partial_settlement_transaction_2.authorized_transaction_id)
-
-        refreshed_authorized_transaction = Transaction.find(authorized_transaction.id)
-        self.assertEqual(2, len(refreshed_authorized_transaction.partial_settlement_transaction_ids))
-
-    def test_transaction_submit_for_partial_settlement_unsuccessful(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            }
-        })
-        self.assertTrue(result.is_success)
-        authorized_transaction = result.transaction
-
-        partial_settlement_result = Transaction.submit_for_partial_settlement(authorized_transaction.id, Decimal("500.00"))
-        partial_settlement_transaction = partial_settlement_result.transaction
-
-        partial_settlement_result_2 = Transaction.submit_for_partial_settlement(partial_settlement_transaction.id, Decimal("250.00"))
-        self.assertFalse(partial_settlement_result_2.is_success)
-        error_code = partial_settlement_result_2.errors.for_object("transaction").on("base")[0].code
-        self.assertEqual(ErrorCodes.Transaction.CannotSubmitForPartialSettlement, error_code)
-
-    def test_submit_for_partial_settlement_with_order_id(self):
-        transaction = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            }
-        }).transaction
-
-        params = {"order_id": "ABC123"}
-
-        submitted_transaction = Transaction.submit_for_partial_settlement(transaction.id, Decimal("900"), params).transaction
-
-        self.assertEqual(Transaction.Status.SubmittedForSettlement, submitted_transaction.status)
-        self.assertEqual("ABC123", submitted_transaction.order_id)
-
-    def test_submit_for_partial_settlement_with_descriptor(self):
-        transaction = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            }
-        }).transaction
-
-        params = {
-            "descriptor": {
-                "name": "123*123456789012345678",
-                "phone": "3334445555",
-                "url": "ebay.com"
-            }
-        }
-
-        submitted_transaction = Transaction.submit_for_partial_settlement(transaction.id, Decimal("900"), params).transaction
-
-        self.assertEqual(Transaction.Status.SubmittedForSettlement, submitted_transaction.status)
-        self.assertEqual("123*123456789012345678", submitted_transaction.descriptor.name)
-        self.assertEqual("3334445555", submitted_transaction.descriptor.phone)
-        self.assertEqual("ebay.com", submitted_transaction.descriptor.url)
-
-    @raises_with_regexp(KeyError, "'Invalid keys: invalid_param'")
-    def test_submit_for_partial_settlement_with_invalid_params(self):
-        transaction = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            }
-        }).transaction
-
-        params = {
-            "descriptor": {
-                "name": "123*123456789012345678",
-                "phone": "3334445555",
-                "url": "ebay.com"
-            },
-            "invalid_param": "foo",
-        }
-
-        Transaction.submit_for_partial_settlement(transaction.id, Decimal("900"), params)
-
-    def test_facilitated_transaction(self):
-        granting_gateway, credit_card = TestHelper.create_payment_method_grant_fixtures()
-        grant_result = granting_gateway.payment_method.grant(credit_card.token, False)
-        nonce = grant_result.payment_method_nonce.nonce
-
-        result = Transaction.sale({
-            "payment_method_nonce": nonce,
-            "amount": TransactionAmounts.Authorize,
-        })
-        self.assertNotEqual(result.transaction.facilitated_details, None)
-        self.assertEqual(result.transaction.facilitated_details.merchant_id, "integration_merchant_id")
-        self.assertEqual(result.transaction.facilitated_details.merchant_name, "14ladders")
-        self.assertEqual(result.transaction.facilitated_details.payment_method_nonce, nonce)
-        self.assertTrue(result.transaction.facilitator_details is not None)
-        self.assertEqual(result.transaction.facilitator_details.oauth_application_client_id, "client_id$development$integration_client_id")
-        self.assertEqual(result.transaction.facilitator_details.oauth_application_name, "PseudoShop")
-        self.assertTrue(result.transaction.billing["postal_code"] is None)
-
-    def test_include_billing_postal_code(self):
-        granting_gateway, credit_card = TestHelper.create_payment_method_grant_fixtures()
-        grant_result = granting_gateway.payment_method.grant(credit_card.token, { "allow_vaulting": False, "include_billing_postal_code": True })
-
-        result = Transaction.sale({
-            "payment_method_nonce": grant_result.payment_method_nonce.nonce,
-            "amount": TransactionAmounts.Authorize,
-        })
-
-        self.assertTrue(result.transaction.billing["postal_code"], "95131")
-
-    def test_shared_vault_transaction_with_nonce(self):
-        config = Configuration(
-            merchant_id="integration_merchant_public_id",
-            public_key="oauth_app_partner_user_public_key",
-            private_key="oauth_app_partner_user_private_key",
-            environment=Environment.Development
-        )
-
-        gateway = BraintreeGateway(config)
-        customer = gateway.customer.create({"first_name": "Bob"}).customer
-        address = gateway.address.create({
-            "customer_id": customer.id,
-            "first_name": "Joe",
-        }).address
-
-        credit_card = gateway.credit_card.create(
-            params={
-                "customer_id": customer.id,
-                "number": "4111111111111111",
-                "expiration_date": "05/2009",
-            }
-        ).credit_card
-
-        shared_nonce = gateway.payment_method_nonce.create(
-            credit_card.token
-        ).payment_method_nonce.nonce
-
-        oauth_app_gateway = BraintreeGateway(
-            client_id="client_id$development$integration_client_id",
-            client_secret="client_secret$development$integration_client_secret",
-            environment=Environment.Development
-        )
-        code = TestHelper.create_grant(oauth_app_gateway, {
-            "merchant_public_id": "integration_merchant_id",
-            "scope": "grant_payment_method,shared_vault_transactions"
-        })
-        access_token = oauth_app_gateway.oauth.create_token_from_code({
-            "code": code
-        }).credentials.access_token
-
-        recipient_gateway = BraintreeGateway(access_token=access_token)
-
-        result = recipient_gateway.transaction.sale({
-            "shared_payment_method_nonce": shared_nonce,
-            "shared_customer_id": customer.id,
-            "shared_shipping_address_id": address.id,
-            "shared_billing_address_id": address.id,
-            "amount": "100"
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertEqual(result.transaction.shipping_details.first_name, address.first_name)
-        self.assertEqual(result.transaction.billing_details.first_name, address.first_name)
-        self.assertEqual(result.transaction.customer_details.first_name, customer.first_name)
-
-    def test_shared_vault_transaction_with_token(self):
-        config = Configuration(
-            merchant_id="integration_merchant_public_id",
-            public_key="oauth_app_partner_user_public_key",
-            private_key="oauth_app_partner_user_private_key",
-            environment=Environment.Development
-        )
-
-        gateway = BraintreeGateway(config)
-        customer = gateway.customer.create({"first_name": "Bob"}).customer
-        address = gateway.address.create({
-            "customer_id": customer.id,
-            "first_name": "Joe",
-        }).address
-
-        credit_card = gateway.credit_card.create(
-            params={
-                "customer_id": customer.id,
-                "number": "4111111111111111",
-                "expiration_date": "05/2009",
-            }
-        ).credit_card
-
-        oauth_app_gateway = BraintreeGateway(
-            client_id="client_id$development$integration_client_id",
-            client_secret="client_secret$development$integration_client_secret",
-            environment=Environment.Development
-        )
-        code = TestHelper.create_grant(oauth_app_gateway, {
-            "merchant_public_id": "integration_merchant_id",
-            "scope": "grant_payment_method,shared_vault_transactions"
-        })
-        access_token = oauth_app_gateway.oauth.create_token_from_code({
-            "code": code
-        }).credentials.access_token
-
-        recipient_gateway = BraintreeGateway(
-            access_token=access_token,
-        )
-
-        result = recipient_gateway.transaction.sale({
-            "shared_payment_method_token": credit_card.token,
-            "shared_customer_id": customer.id,
-            "shared_shipping_address_id": address.id,
-            "shared_billing_address_id": address.id,
-            "amount": "100"
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertEqual(result.transaction.shipping_details.first_name, address.first_name)
-        self.assertEqual(result.transaction.billing_details.first_name, address.first_name)
-        self.assertEqual(result.transaction.customer_details.first_name, customer.first_name)
-
-    def test_sale_elo_card(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "merchant_account_id": TestHelper.adyen_merchant_account_id,
-            "credit_card": {
-                "number": CreditCardNumbers.Elo,
-                "expiration_date": "10/2020",
-                "cvv": "737",
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-        self.assertEqual(TestHelper.adyen_merchant_account_id, transaction.merchant_account_id)
-
-    def test_sale_hiper_card(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "merchant_account_id": TestHelper.hiper_brl_merchant_account_id,
-            "credit_card": {
-                "number": CreditCardNumbers.Hiper,
-                "expiration_date": "10/2020",
-                "cvv": "737",
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-        self.assertEqual(CreditCard.CardType.Hiper, transaction.credit_card_details.card_type)
-
-    def test_sale_hipercard_card(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "merchant_account_id": TestHelper.hiper_brl_merchant_account_id,
-            "credit_card": {
-                "number": CreditCardNumbers.Hipercard,
-                "expiration_date": "10/2020",
-                "cvv": "737",
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-        self.assertEqual(CreditCard.CardType.Hipercard, transaction.credit_card_details.card_type)
-
-    def test_account_type_credit(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "merchant_account_id": TestHelper.hiper_brl_merchant_account_id,
-            "credit_card": {
-                "number": CreditCardNumbers.Hipercard,
-                "expiration_date": "10/2020",
-                "cvv": "737",
-            },
-            "options": {
-                "credit_card": {
-                    "account_type": "credit",
-                },
-            },
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-        self.assertEqual("credit", transaction.credit_card_details.account_type)
-
-    def test_account_type_debit(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "merchant_account_id": TestHelper.hiper_brl_merchant_account_id,
-            "credit_card": {
-                "number": CreditCardNumbers.Hiper,
-                "expiration_date": "10/2020",
-                "cvv": "737",
-            },
-            "options": {
-                "submit_for_settlement": True,
-                "credit_card": {
-                    "account_type": "debit",
-                },
-            },
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-        self.assertEqual("debit", transaction.credit_card_details.account_type)
-
-    def test_account_type_debit_error_does_not_support_auths(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "merchant_account_id": TestHelper.hiper_brl_merchant_account_id,
-            "credit_card": {
-                "number": CreditCardNumbers.Hiper,
-                "expiration_date": "10/2020",
-                "cvv": "737",
-            },
-            "options": {
-                "credit_card": {
-                    "account_type": "debit",
-                },
-            },
-        })
-
-        self.assertFalse(result.is_success)
-        self.assertEqual(
-            ErrorCodes.Transaction.Options.CreditCard.AccountTypeDebitDoesNotSupportAuths,
-            result.errors.for_object("transaction").for_object("options").for_object("credit_card").on("account_type")[0].code
-            )
-
-    def test_account_type_debit_error_account_type_is_invalid(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "merchant_account_id": TestHelper.hiper_brl_merchant_account_id,
-            "credit_card": {
-                "number": CreditCardNumbers.Hiper,
-                "expiration_date": "10/2020",
-                "cvv": "737",
-                },
-            "options": {
-                "credit_card": {
-                    "account_type": "invalid",
-                    },
-                },
-            })
-        self.assertFalse(result.is_success)
-        self.assertEqual(
-          ErrorCodes.Transaction.Options.CreditCard.AccountTypeIsInvalid,
-          result.errors.for_object("transaction").for_object("options").for_object("credit_card").on("account_type")[0].code
-        )
-
-    def test_account_type_debit_error_account_type_not_supported(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": CreditCardNumbers.MasterCard,
-                "expiration_date": "05/2009"
-            },
-            "options": {
-                "credit_card": {
-                    "account_type": "debit",
-                    },
-                },
-            })
-        self.assertFalse(result.is_success)
-        self.assertEqual(
-          ErrorCodes.Transaction.Options.CreditCard.AccountTypeNotSupported,
-          result.errors.for_object("transaction").for_object("options").for_object("credit_card").on("account_type")[0].code
-        )
-
-    def test_paypal_here_details_auth_capture(self):
-        result = Transaction.find('paypal_here_auth_capture_id')
-        self.assertIsNotNone(result.paypal_here_details)
-        self.assertEqual(PaymentInstrumentType.PayPalHere, result.payment_instrument_type)
-
-        details = result.paypal_here_details
-        self.assertIsNotNone(details.authorization_id)
-        self.assertIsNotNone(details.capture_id)
-        self.assertIsNotNone(details.invoice_id)
-        self.assertIsNotNone(details.last_4)
-        self.assertIsNotNone(details.payment_type)
-        self.assertIsNotNone(details.transaction_fee_amount)
-        self.assertIsNotNone(details.transaction_fee_currency_iso_code)
-        self.assertIsNotNone(details.transaction_initiation_date)
-        self.assertIsNotNone(details.transaction_updated_date)
-
-    def test_paypal_here_details_sale(self):
-        result = Transaction.find('paypal_here_sale_id')
-        self.assertIsNotNone(result.paypal_here_details)
-
-        details = result.paypal_here_details
-        self.assertIsNotNone(details.payment_id)
-
-    def test_paypal_here_details_refund(self):
-        result = Transaction.find('paypal_here_refund_id')
-        self.assertIsNotNone(result.paypal_here_details)
-
-        details = result.paypal_here_details
-        self.assertIsNotNone(details.refund_id)
-
-    def test_sale_network_response_code_text(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009"
-            },
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-        self.assertEqual("1000", transaction.processor_response_code)
-        self.assertEqual(ProcessorResponseTypes.Approved, transaction.processor_response_type)
-        self.assertEqual("XX", transaction.network_response_code)
-        self.assertEqual("sample network response text", transaction.network_response_text)
diff --git a/tests/integration/test_transaction_line_item.py b/tests/integration/test_transaction_line_item.py
deleted file mode 100644
index db9f3d2..0000000
--- a/tests/integration/test_transaction_line_item.py
+++ /dev/null
@@ -1,29 +0,0 @@
-from tests.test_helper import *
-from braintree.test.credit_card_numbers import CreditCardNumbers
-
-class TestTransactionLineItem(unittest.TestCase):
-
-    def test_transaction_line_item_find_all_returns_line_items(self):
-        transaction = Transaction.sale({
-            "amount": "35.05",
-            "credit_card": {
-                "number": CreditCardNumbers.Visa,
-                "expiration_date": "05/2009",
-            },
-            "line_items": [{
-                "quantity": "1.0232",
-                "name": "Name #1",
-                "kind": TransactionLineItem.Kind.Debit,
-                "unit_amount": "45.1232",
-                "total_amount": "45.15",
-            }]
-        }).transaction
-
-        line_items = TransactionLineItem.find_all(transaction.id)
-        self.assertEqual(1, len(line_items))
-        lineItem = line_items[0]
-        self.assertEqual("1.0232", lineItem.quantity)
-        self.assertEqual("Name #1", lineItem.name)
-        self.assertEqual(TransactionLineItem.Kind.Debit, lineItem.kind)
-        self.assertEqual("45.1232", lineItem.unit_amount)
-        self.assertEqual("45.15", lineItem.total_amount)
diff --git a/tests/integration/test_transaction_line_item_gateway.py b/tests/integration/test_transaction_line_item_gateway.py
deleted file mode 100644
index 5a98393..0000000
--- a/tests/integration/test_transaction_line_item_gateway.py
+++ /dev/null
@@ -1,8 +0,0 @@
-from tests.test_helper import *
-
-class TestTransactionLineItemGateway(unittest.TestCase):
-
-    @raises(NotFoundError)
-    def test_transaction_line_item_gateway_find_all_raises_when_transaction_not_found(self):
-        transaction_id = "willnotbefound"
-        TransactionLineItem.find_all(transaction_id)
diff --git a/tests/integration/test_transaction_search.py b/tests/integration/test_transaction_search.py
deleted file mode 100644
index d4bf414..0000000
--- a/tests/integration/test_transaction_search.py
+++ /dev/null
@@ -1,1581 +0,0 @@
-from tests.test_helper import *
-
-class TestTransactionSearch(unittest.TestCase):
-    def test_advanced_search_no_results(self):
-        collection = Transaction.search([
-            TransactionSearch.billing_first_name == "no_such_person"
-        ])
-        self.assertEqual(0, collection.maximum_size)
-
-    def test_advanced_search_searches_all_text_fields_at_once(self):
-        first_name = "Tim%s" % random.randint(1, 100000)
-        token = "creditcard%s" % random.randint(1, 100000)
-        customer_id = "customer%s" % random.randint(1, 100000)
-
-        transaction = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2012",
-                "cardholder_name": "Tom Smith",
-                "token": token,
-            },
-            "billing": {
-                "company": "Braintree",
-                "country_name": "United States of America",
-                "extended_address": "Suite 123",
-                "first_name": first_name,
-                "last_name": "Smith",
-                "locality": "Chicago",
-                "postal_code": "12345",
-                "region": "IL",
-                "street_address": "123 Main St"
-            },
-            "customer": {
-                "company": "Braintree",
-                "email": "smith@example.com",
-                "fax": "5551231234",
-                "first_name": "Tom",
-                "id": customer_id,
-                "last_name": "Smith",
-                "phone": "5551231234",
-                "website": "http://example.com",
-            },
-            "options": {
-                "store_in_vault": True,
-                "submit_for_settlement": True
-            },
-            "order_id": "myorder",
-            "shipping": {
-                "company": "Braintree P.S.",
-                "country_name": "Mexico",
-                "extended_address": "Apt 456",
-                "first_name": "Thomas",
-                "last_name": "Smithy",
-                "locality": "Braintree",
-                "postal_code": "54321",
-                "region": "MA",
-                "street_address": "456 Road"
-            }
-        }).transaction
-
-        TestHelper.settle_transaction(transaction.id)
-        transaction = Transaction.find(transaction.id)
-
-        collection = Transaction.search([
-            TransactionSearch.billing_company == "Braintree",
-            TransactionSearch.billing_country_name == "United States of America",
-            TransactionSearch.billing_extended_address == "Suite 123",
-            TransactionSearch.billing_first_name == first_name,
-            TransactionSearch.billing_last_name == "Smith",
-            TransactionSearch.billing_locality == "Chicago",
-            TransactionSearch.billing_postal_code == "12345",
-            TransactionSearch.billing_region == "IL",
-            TransactionSearch.billing_street_address == "123 Main St",
-            TransactionSearch.credit_card_cardholder_name == "Tom Smith",
-            TransactionSearch.credit_card_expiration_date == "05/2012",
-            TransactionSearch.credit_card_number == "4111111111111111",
-            TransactionSearch.customer_company == "Braintree",
-            TransactionSearch.customer_email == "smith@example.com",
-            TransactionSearch.customer_fax == "5551231234",
-            TransactionSearch.customer_first_name == "Tom",
-            TransactionSearch.customer_id == customer_id,
-            TransactionSearch.customer_last_name == "Smith",
-            TransactionSearch.customer_phone == "5551231234",
-            TransactionSearch.customer_website == "http://example.com",
-            TransactionSearch.order_id == "myorder",
-            TransactionSearch.payment_method_token == token,
-            TransactionSearch.processor_authorization_code == transaction.processor_authorization_code,
-            TransactionSearch.settlement_batch_id == transaction.settlement_batch_id,
-            TransactionSearch.shipping_company == "Braintree P.S.",
-            TransactionSearch.shipping_country_name == "Mexico",
-            TransactionSearch.shipping_extended_address == "Apt 456",
-            TransactionSearch.shipping_first_name == "Thomas",
-            TransactionSearch.shipping_last_name == "Smithy",
-            TransactionSearch.shipping_locality == "Braintree",
-            TransactionSearch.shipping_postal_code == "54321",
-            TransactionSearch.shipping_region == "MA",
-            TransactionSearch.shipping_street_address == "456 Road",
-            TransactionSearch.id == transaction.id
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(transaction.id, collection.first.id)
-
-    def test_advanced_search_search_each_text_field(self):
-        first_name = "Tim%s" % random.randint(1, 100000)
-        token = "creditcard%s" % random.randint(1, 100000)
-        customer_id = "customer%s" % random.randint(1, 100000)
-
-        transaction = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2012",
-                "cardholder_name": "Tom Smith",
-                "token": token,
-            },
-            "billing": {
-                "company": "Braintree",
-                "country_name": "United States of America",
-                "extended_address": "Suite 123",
-                "first_name": first_name,
-                "last_name": "Smith",
-                "locality": "Chicago",
-                "postal_code": "12345",
-                "region": "IL",
-                "street_address": "123 Main St"
-            },
-            "customer": {
-                "company": "Braintree",
-                "email": "smith@example.com",
-                "fax": "5551231234",
-                "first_name": "Tom",
-                "id": customer_id,
-                "last_name": "Smith",
-                "phone": "5551231234",
-                "website": "http://example.com",
-            },
-            "options": {
-                "store_in_vault": True
-            },
-            "order_id": "myorder",
-            "shipping": {
-                "company": "Braintree P.S.",
-                "country_name": "Mexico",
-                "extended_address": "Apt 456",
-                "first_name": "Thomas",
-                "last_name": "Smithy",
-                "locality": "Braintree",
-                "postal_code": "54321",
-                "region": "MA",
-                "street_address": "456 Road"
-            }
-        }).transaction
-
-        search_criteria = {
-            "billing_company": "Braintree",
-            "billing_country_name": "United States of America",
-            "billing_extended_address": "Suite 123",
-            "billing_first_name": first_name,
-            "billing_last_name": "Smith",
-            "billing_locality": "Chicago",
-            "billing_postal_code": "12345",
-            "billing_region": "IL",
-            "billing_street_address": "123 Main St",
-            "credit_card_cardholder_name": "Tom Smith",
-            "credit_card_expiration_date": "05/2012",
-            "credit_card_number": "4111111111111111",
-            "customer_company": "Braintree",
-            "customer_email": "smith@example.com",
-            "customer_fax": "5551231234",
-            "customer_first_name": "Tom",
-            "customer_id": customer_id,
-            "customer_last_name": "Smith",
-            "customer_phone": "5551231234",
-            "customer_website": "http://example.com",
-            "order_id": "myorder",
-            "payment_method_token": token,
-            "processor_authorization_code": transaction.processor_authorization_code,
-            "shipping_company": "Braintree P.S.",
-            "shipping_country_name": "Mexico",
-            "shipping_extended_address": "Apt 456",
-            "shipping_first_name": "Thomas",
-            "shipping_last_name": "Smithy",
-            "shipping_locality": "Braintree",
-            "shipping_postal_code": "54321",
-            "shipping_region": "MA",
-            "shipping_street_address": "456 Road",
-            "user": "integration_user_public_id",
-            "credit_card_unique_identifier": transaction.credit_card["unique_number_identifier"]
-        }
-
-        for criterion, value in search_criteria.items():
-            text_node = getattr(TransactionSearch, criterion)
-
-            collection = Transaction.search([
-                TransactionSearch.id == transaction.id,
-                text_node == value
-            ])
-            self.assertEqual(1, collection.maximum_size)
-            self.assertEqual(transaction.id, collection.first.id)
-
-            collection = Transaction.search([
-                TransactionSearch.id == transaction.id,
-                text_node == "invalid"
-            ])
-            self.assertEqual(0, collection.maximum_size)
-
-    def test_advanced_search_with_argument_list_rather_than_literal_list(self):
-        transaction = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2012",
-                "cardholder_name": "Tom Smith",
-            },
-        }).transaction
-
-        collection = Transaction.search(
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.credit_card_cardholder_name == "Tom Smith"
-        )
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(transaction.id, collection.first.id)
-
-    def test_advanced_search_with_payment_instrument_type_is_credit_card(self):
-        transaction = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2012",
-                "cardholder_name": "Tom Smith",
-            },
-        }).transaction
-
-        collection = Transaction.search(
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.payment_instrument_type == "CreditCardDetail"
-        )
-
-        self.assertEqual(transaction.payment_instrument_type, PaymentInstrumentType.CreditCard)
-        self.assertEqual(transaction.id, collection.first.id)
-
-    def test_advanced_search_with_payment_instrument_type_is_paypal(self):
-        transaction = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "payment_method_nonce": Nonces.PayPalOneTimePayment
-        }).transaction
-
-        collection = Transaction.search(
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.payment_instrument_type == "PayPalDetail"
-        )
-
-        self.assertEqual(transaction.payment_instrument_type, PaymentInstrumentType.PayPalAccount)
-        self.assertEqual(transaction.id, collection.first.id)
-
-    def test_advanced_search_with_payment_instrument_type_is_local_payment(self):
-        transaction = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "options": {
-                "submit_for_settlement": True,
-            },
-            "payment_method_nonce": Nonces.LocalPayment
-        }).transaction
-
-        collection = Transaction.search(
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.payment_instrument_type == "LocalPaymentDetail"
-        )
-
-        self.assertEqual(transaction.payment_instrument_type, PaymentInstrumentType.LocalPayment)
-        self.assertEqual(transaction.id, collection.first.id)
-
-    def test_advanced_search_with_payment_instrument_type_is_apple_pay(self):
-        transaction = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "payment_method_nonce": Nonces.ApplePayVisa
-        }).transaction
-
-        collection = Transaction.search(
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.payment_instrument_type == "ApplePayDetail"
-        )
-
-        self.assertEqual(transaction.payment_instrument_type, PaymentInstrumentType.ApplePayCard)
-        self.assertEqual(transaction.id, collection.first.id)
-
-    def test_advanced_search_text_node_contains(self):
-        transaction = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2012",
-                "cardholder_name": "Jane Shea"
-            }
-        }).transaction
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.credit_card_cardholder_name.contains("ane She")
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(transaction.id, collection.first.id)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.credit_card_cardholder_name.contains("invalid")
-        ])
-
-        self.assertEqual(0, collection.maximum_size)
-
-    def test_advanced_search_text_node_starts_with(self):
-        transaction = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2012",
-                "cardholder_name": "Jane Shea"
-            }
-        }).transaction
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.credit_card_cardholder_name.starts_with("Jane S")
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(transaction.id, collection.first.id)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.credit_card_cardholder_name.starts_with("invalid")
-        ])
-
-        self.assertEqual(0, collection.maximum_size)
-
-    def test_advanced_search_text_node_ends_with(self):
-        transaction = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2012",
-                "cardholder_name": "Jane Shea"
-            }
-        }).transaction
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.credit_card_cardholder_name.ends_with("e Shea")
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(transaction.id, collection.first.id)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.credit_card_cardholder_name.ends_with("invalid")
-        ])
-
-        self.assertEqual(0, collection.maximum_size)
-
-    def test_advanced_search_text_node_is_not(self):
-        transaction = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2012",
-                "cardholder_name": "Jane Shea"
-            }
-        }).transaction
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.credit_card_cardholder_name != "invalid"
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(transaction.id, collection.first.id)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.credit_card_cardholder_name != "Jane Shea"
-        ])
-
-        self.assertEqual(0, collection.maximum_size)
-
-    def test_advanced_search_multiple_value_node_created_using(self):
-        transaction = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2012"
-            }
-        }).transaction
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.created_using == Transaction.CreatedUsing.FullInformation
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(transaction.id, collection.first.id)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.created_using.in_list([Transaction.CreatedUsing.FullInformation, Transaction.CreatedUsing.Token])
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(transaction.id, collection.first.id)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.created_using == Transaction.CreatedUsing.Token
-        ])
-
-        self.assertEqual(0, collection.maximum_size)
-
-    @raises_with_regexp(AttributeError, "Invalid argument\(s\) for created_using: noSuchCreatedUsing")
-    def test_advanced_search_multiple_value_node_allowed_values_created_using(self):
-        Transaction.search([TransactionSearch.created_using == "noSuchCreatedUsing"])
-
-    def test_advanced_search_multiple_value_node_credit_card_customer_location(self):
-        transaction = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2012"
-            }
-        }).transaction
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.credit_card_customer_location == CreditCard.CustomerLocation.US
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(transaction.id, collection.first.id)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.credit_card_customer_location.in_list([CreditCard.CustomerLocation.US, CreditCard.CustomerLocation.International])
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(transaction.id, collection.first.id)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.credit_card_customer_location == CreditCard.CustomerLocation.International
-        ])
-
-        self.assertEqual(0, collection.maximum_size)
-
-    @raises_with_regexp(AttributeError,
-            "Invalid argument\(s\) for credit_card_customer_location: noSuchCreditCardCustomerLocation")
-    def test_advanced_search_multiple_value_node_allowed_values_credit_card_customer_location(self):
-        Transaction.search([
-            TransactionSearch.credit_card_customer_location == "noSuchCreditCardCustomerLocation"
-        ])
-
-    def test_advanced_search_multiple_value_node_merchant_account_id(self):
-        transaction = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2012"
-            }
-        }).transaction
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.merchant_account_id == transaction.merchant_account_id
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(transaction.id, collection.first.id)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.merchant_account_id.in_list([transaction.merchant_account_id, "bogus_merchant_account_id"])
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(transaction.id, collection.first.id)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.merchant_account_id == "bogus_merchant_account_id"
-        ])
-
-        self.assertEqual(0, collection.maximum_size)
-
-    def test_advanced_search_multiple_value_node_credit_card_card_type(self):
-        transaction = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2012"
-            }
-        }).transaction
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.credit_card_card_type == transaction.credit_card_details.card_type
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(transaction.id, collection.first.id)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.credit_card_card_type.in_list([transaction.credit_card_details.card_type, CreditCard.CardType.AmEx])
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(transaction.id, collection.first.id)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.credit_card_card_type == CreditCard.CardType.AmEx
-        ])
-
-        self.assertEqual(0, collection.maximum_size)
-
-    def test_advanced_search_elo(self):
-        transaction = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "merchant_account_id": TestHelper.adyen_merchant_account_id,
-            "credit_card": {
-                "number": CreditCardNumbers.Elo,
-                "expiration_date": "10/2020",
-                "cvv": "737",
-            }
-        }).transaction
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.credit_card_card_type == transaction.credit_card_details.card_type
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(transaction.id, collection.first.id)
-        self.assertEqual(transaction.credit_card_details.card_type, collection.first.credit_card_details.card_type)
-
-    def test_advanced_search_hiper(self):
-        transaction = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "merchant_account_id": TestHelper.hiper_brl_merchant_account_id,
-            "credit_card": {
-                "number": CreditCardNumbers.Hiper,
-                "expiration_date": "10/2020",
-                "cvv": "737",
-            }
-        }).transaction
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.credit_card_card_type == transaction.credit_card_details.card_type
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(transaction.id, collection.first.id)
-        self.assertEqual(transaction.credit_card_details.card_type, collection.first.credit_card_details.card_type)
-
-    def test_advanced_search_hipercard(self):
-        transaction = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "merchant_account_id": TestHelper.hiper_brl_merchant_account_id,
-            "credit_card": {
-                "number": CreditCardNumbers.Hipercard,
-                "expiration_date": "10/2020",
-                "cvv": "737",
-            }
-        }).transaction
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.credit_card_card_type == transaction.credit_card_details.card_type
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(transaction.id, collection.first.id)
-        self.assertEqual(transaction.credit_card_details.card_type, collection.first.credit_card_details.card_type)
-
-    @raises_with_regexp(AttributeError,
-            "Invalid argument\(s\) for credit_card_card_type: noSuchCreditCardCardType")
-    def test_advanced_search_multiple_value_node_allowed_values_credit_card_card_type(self):
-        Transaction.search([
-            TransactionSearch.credit_card_card_type == "noSuchCreditCardCardType"
-        ])
-
-    def test_advanced_search_multiple_value_node_status(self):
-        transaction = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2012"
-            }
-        }).transaction
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.status == Transaction.Status.Authorized
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(transaction.id, collection.first.id)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.status.in_list([Transaction.Status.Authorized, Transaction.Status.Settled])
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(transaction.id, collection.first.id)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.status == Transaction.Status.Settled
-        ])
-
-        self.assertEqual(0, collection.maximum_size)
-
-    def test_advanced_search_authorization_expired_status(self):
-        collection = Transaction.search(
-            TransactionSearch.status == Transaction.Status.AuthorizationExpired
-        )
-
-        self.assertTrue(collection.maximum_size > 0)
-        self.assertEqual(Transaction.Status.AuthorizationExpired, collection.first.status)
-
-    def test_advanced_search_allows_new_settlement_statuses(self):
-        collection = Transaction.search([
-            TransactionSearch.status.in_list(["settlement_confirmed", "settlement_declined"])
-        ])
-        print(collection)
-
-    @raises_with_regexp(AttributeError, "Invalid argument\(s\) for status: noSuchStatus")
-    def test_advanced_search_multiple_value_node_allowed_values_status(self):
-        Transaction.search([TransactionSearch.status == "noSuchStatus"])
-
-    def test_advanced_search_multiple_value_node_source(self):
-        transaction = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2012"
-            }
-        }).transaction
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.source == Transaction.Source.Api
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(transaction.id, collection.first.id)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.source.in_list([Transaction.Source.Api, Transaction.Source.ControlPanel])
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(transaction.id, collection.first.id)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.source == Transaction.Source.ControlPanel
-        ])
-
-        self.assertEqual(0, collection.maximum_size)
-
-    def test_advanced_search_multiple_value_node_type(self):
-        transaction = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2012"
-            }
-        }).transaction
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.type == Transaction.Type.Sale
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(transaction.id, collection.first.id)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.type.in_list([Transaction.Type.Sale, Transaction.Type.Credit])
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(transaction.id, collection.first.id)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.type == Transaction.Type.Credit
-        ])
-
-        self.assertEqual(0, collection.maximum_size)
-
-    @raises_with_regexp(AttributeError, "Invalid argument\(s\) for type: noSuchType")
-    def test_advanced_search_multiple_value_node_allowed_values_type(self):
-        Transaction.search([
-            TransactionSearch.type == "noSuchType"
-        ])
-
-    def test_advanced_search_multiple_value_node_type_with_refund(self):
-        name = "Anabel Atkins%s" % random.randint(1, 100000)
-        sale = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2012",
-                "cardholder_name": name
-            },
-            'options': {
-                'submit_for_settlement': True
-            }
-        }).transaction
-        TestHelper.settle_transaction(sale.id)
-
-        refund = Transaction.refund(sale.id).transaction
-
-        credit = Transaction.credit({
-            "amount": Decimal(TransactionAmounts.Authorize),
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2009",
-                "cardholder_name": name
-            }
-        }).transaction
-
-        collection = Transaction.search([
-            TransactionSearch.credit_card_cardholder_name == name,
-            TransactionSearch.type == Transaction.Type.Credit
-        ])
-
-        self.assertEqual(2, collection.maximum_size)
-
-        collection = Transaction.search([
-            TransactionSearch.credit_card_cardholder_name == name,
-            TransactionSearch.type == Transaction.Type.Credit,
-            TransactionSearch.refund == True
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(refund.id, collection.first.id)
-
-        collection = Transaction.search([
-            TransactionSearch.credit_card_cardholder_name == name,
-            TransactionSearch.type == Transaction.Type.Credit,
-            TransactionSearch.refund == False
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(credit.id, collection.first.id)
-
-    def test_advanced_search_range_node_amount(self):
-        name = "Henrietta Livingston%s" % random.randint(1, 100000)
-        t_1000 = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2012",
-                "cardholder_name": name
-            }
-        }).transaction
-
-        t_1500 = Transaction.sale({
-            "amount": "1500.00",
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2012",
-                "cardholder_name": name
-            }
-        }).transaction
-
-        t_1800 = Transaction.sale({
-            "amount": "1800.00",
-            "credit_card": {
-                "number": "4111111111111111",
-                "expiration_date": "05/2012",
-                "cardholder_name": name
-            }
-        }).transaction
-
-        collection = Transaction.search([
-            TransactionSearch.credit_card_cardholder_name == name,
-            TransactionSearch.amount >= "1700"
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(t_1800.id, collection.first.id)
-
-        collection = Transaction.search([
-            TransactionSearch.credit_card_cardholder_name == name,
-            TransactionSearch.amount <= "1250"
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(t_1000.id, collection.first.id)
-
-        collection = Transaction.search([
-            TransactionSearch.credit_card_cardholder_name == name,
-            TransactionSearch.amount.between("1100", "1600")
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(t_1500.id, collection.first.id)
-
-    def test_advanced_search_range_node_created_at_less_than_or_equal_to(self):
-        transaction = Transaction.sale({
-             "amount": TransactionAmounts.Authorize,
-             "credit_card": {
-                 "number": "4111111111111111",
-                 "expiration_date": "05/2012"
-             }
-         }).transaction
-
-        past = transaction.created_at - timedelta(minutes=10)
-        now = transaction.created_at
-        future = transaction.created_at + timedelta(minutes=10)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.created_at <= past
-        ])
-
-        self.assertEqual(0, collection.maximum_size)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.created_at <= now
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(transaction.id, collection.first.id)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.created_at <= future
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(transaction.id, collection.first.id)
-
-    def test_advanced_search_range_node_created_at_greater_than_or_equal_to(self):
-        transaction = Transaction.sale({
-             "amount": TransactionAmounts.Authorize,
-             "credit_card": {
-                 "number": "4111111111111111",
-                 "expiration_date": "05/2012"
-             }
-         }).transaction
-
-        past = transaction.created_at - timedelta(minutes=10)
-        now = transaction.created_at
-        future = transaction.created_at + timedelta(minutes=10)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.created_at >= past
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(transaction.id, collection.first.id)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.created_at >= now
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(transaction.id, collection.first.id)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.created_at >= future
-        ])
-
-        self.assertEqual(0, collection.maximum_size)
-
-    def test_advanced_search_range_node_created_at_between(self):
-        transaction = Transaction.sale({
-             "amount": TransactionAmounts.Authorize,
-             "credit_card": {
-                 "number": "4111111111111111",
-                 "expiration_date": "05/2012"
-             }
-         }).transaction
-
-        past = transaction.created_at - timedelta(minutes=10)
-        now = transaction.created_at
-        future = transaction.created_at + timedelta(minutes=10)
-        future2 = transaction.created_at + timedelta(minutes=20)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.created_at.between(past, now)
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(transaction.id, collection.first.id)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.created_at.between(now, future)
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(transaction.id, collection.first.id)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.created_at.between(past, future)
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(transaction.id, collection.first.id)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.created_at.between(future, future2)
-        ])
-
-        self.assertEqual(0, collection.maximum_size)
-
-    def test_advanced_search_range_node_created_at_is(self):
-        transaction = Transaction.sale({
-             "amount": TransactionAmounts.Authorize,
-             "credit_card": {
-                 "number": "4111111111111111",
-                 "expiration_date": "05/2012"
-             }
-         }).transaction
-
-        past = transaction.created_at - timedelta(minutes=10)
-        now = transaction.created_at
-        future = transaction.created_at + timedelta(minutes=10)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.created_at == past
-        ])
-
-        self.assertEqual(0, collection.maximum_size)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.created_at == now
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(transaction.id, collection.first.id)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.created_at == future
-        ])
-
-        self.assertEqual(0, collection.maximum_size)
-
-    def test_advanced_search_range_node_created_with_dates(self):
-        transaction = Transaction.sale({
-             "amount": TransactionAmounts.Authorize,
-             "credit_card": {
-                 "number": "4111111111111111",
-                 "expiration_date": "05/2012"
-             }
-         }).transaction
-
-        past = datetime.today() - timedelta(days=1)
-        future = datetime.today() + timedelta(days=1)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.created_at.between(past, future)
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(transaction.id, collection.first.id)
-
-    def test_advanced_search_range_node_disbursement_date_less_than_or_equal_to(self):
-        transaction_id = "deposittransaction"
-        disbursement_time = datetime(2013, 4, 10, 0, 0, 0)
-        past = disbursement_time - timedelta(minutes=10)
-        future = disbursement_time + timedelta(minutes=10)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction_id,
-            TransactionSearch.disbursement_date <= past
-        ])
-
-        self.assertEqual(0, collection.maximum_size)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction_id,
-            TransactionSearch.disbursement_date <= disbursement_time
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(transaction_id, collection.first.id)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction_id,
-            TransactionSearch.disbursement_date <= future
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(transaction_id, collection.first.id)
-
-    def test_advanced_search_range_node_disbursement_date_greater_than_or_equal_to(self):
-        transaction_id = "deposittransaction"
-        disbursement_time = datetime(2013, 4, 10, 0, 0, 0)
-        past = disbursement_time - timedelta(minutes=10)
-        future = disbursement_time + timedelta(days=1)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction_id,
-            TransactionSearch.disbursement_date >= past
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(transaction_id, collection.first.id)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction_id,
-            TransactionSearch.disbursement_date >= disbursement_time
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(transaction_id, collection.first.id)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction_id,
-            TransactionSearch.disbursement_date >= future
-        ])
-
-        self.assertEqual(0, collection.maximum_size)
-
-    def test_advanced_search_range_node_disbursement_date_between(self):
-        transaction_id = "deposittransaction"
-        disbursement_time = datetime(2013, 4, 10, 0, 0, 0)
-        past = disbursement_time - timedelta(days=1)
-        future = disbursement_time + timedelta(days=1)
-        future2 = disbursement_time + timedelta(days=2)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction_id,
-            TransactionSearch.disbursement_date.between(past, disbursement_time)
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(transaction_id, collection.first.id)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction_id,
-            TransactionSearch.disbursement_date.between(disbursement_time, future)
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(transaction_id, collection.first.id)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction_id,
-            TransactionSearch.disbursement_date.between(past, future)
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(transaction_id, collection.first.id)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction_id,
-            TransactionSearch.disbursement_date.between(future, future2)
-        ])
-
-        self.assertEqual(0, collection.maximum_size)
-
-    def test_advanced_search_range_node_disbursement_date_is(self):
-        transaction_id = "deposittransaction"
-        disbursement_time = datetime(2013, 4, 10, 0, 0, 0)
-        past = disbursement_time - timedelta(days=10)
-        now = disbursement_time
-        future = disbursement_time + timedelta(days=10)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction_id,
-            TransactionSearch.disbursement_date == past
-        ])
-
-        self.assertEqual(0, collection.maximum_size)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction_id,
-            TransactionSearch.disbursement_date == now
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(transaction_id, collection.first.id)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction_id,
-            TransactionSearch.disbursement_date == future
-        ])
-
-        self.assertEqual(0, collection.maximum_size)
-
-    def test_advanced_search_range_node_disbursement_date_with_dates(self):
-        transaction_id = "deposittransaction"
-        disbursement_date = date(2013, 4, 10)
-        past = disbursement_date - timedelta(days=1)
-        future = disbursement_date + timedelta(days=1)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction_id,
-            TransactionSearch.disbursement_date.between(past, future)
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(transaction_id, collection.first.id)
-
-    def test_advanced_search_range_node_disputed_date_less_than_or_equal_to(self):
-        disputed_transaction_id = TestHelper.create_disputed_transaction().id
-        disputed_time = datetime.now()
-        past = disputed_time - timedelta(days=1)
-        future = disputed_time + timedelta(minutes=10)
-
-        collection = Transaction.search([
-            TransactionSearch.id == disputed_transaction_id,
-            TransactionSearch.dispute_date <= past
-        ])
-
-        self.assertEqual(0, collection.maximum_size)
-
-        collection = Transaction.search([
-            TransactionSearch.id == disputed_transaction_id,
-            TransactionSearch.dispute_date <= disputed_time
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(disputed_transaction_id, collection.first.id)
-
-        collection = Transaction.search([
-            TransactionSearch.id == disputed_transaction_id,
-            TransactionSearch.dispute_date <= future
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(disputed_transaction_id, collection.first.id)
-
-    def test_advanced_search_range_node_disputed_date_greater_than_or_equal_to(self):
-        disputed_transaction_id = TestHelper.create_disputed_transaction().id
-        disputed_time = datetime.now()
-        past = disputed_time - timedelta(minutes=10)
-        future = disputed_time + timedelta(days=1)
-
-        collection = Transaction.search([
-            TransactionSearch.id == disputed_transaction_id,
-            TransactionSearch.dispute_date >= past
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(disputed_transaction_id, collection.first.id)
-
-        collection = Transaction.search([
-            TransactionSearch.id == disputed_transaction_id,
-            TransactionSearch.dispute_date >= disputed_time
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(disputed_transaction_id, collection.first.id)
-
-        collection = Transaction.search([
-            TransactionSearch.id == disputed_transaction_id,
-            TransactionSearch.dispute_date >= future
-        ])
-
-        self.assertEqual(0, collection.maximum_size)
-
-    def test_advanced_search_range_node_disputed_date_between(self):
-        disputed_transaction_id = TestHelper.create_disputed_transaction().id
-        disputed_time = datetime.now()
-        past = disputed_time - timedelta(days=1)
-        future = disputed_time + timedelta(days=1)
-        future2 = disputed_time + timedelta(days=2)
-
-        collection = Transaction.search([
-            TransactionSearch.id == disputed_transaction_id,
-            TransactionSearch.dispute_date.between(past, disputed_time)
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(disputed_transaction_id, collection.first.id)
-
-        collection = Transaction.search([
-            TransactionSearch.id == disputed_transaction_id,
-            TransactionSearch.dispute_date.between(disputed_time, future)
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(disputed_transaction_id, collection.first.id)
-
-        collection = Transaction.search([
-            TransactionSearch.id == disputed_transaction_id,
-            TransactionSearch.dispute_date.between(past, future)
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(disputed_transaction_id, collection.first.id)
-
-        collection = Transaction.search([
-            TransactionSearch.id == disputed_transaction_id,
-            TransactionSearch.dispute_date.between(future, future2)
-        ])
-
-        self.assertEqual(0, collection.maximum_size)
-
-    def test_advanced_search_range_node_disputed_date_is(self):
-        disputed_transaction_id = TestHelper.create_disputed_transaction().id
-        disputed_date = datetime.today()
-        past = disputed_date - timedelta(days=10)
-        future = disputed_date + timedelta(days=10)
-
-        collection = Transaction.search([
-            TransactionSearch.id == disputed_transaction_id,
-            TransactionSearch.dispute_date == past
-        ])
-
-        self.assertEqual(0, collection.maximum_size)
-
-        collection = Transaction.search([
-            TransactionSearch.id == disputed_transaction_id,
-            TransactionSearch.dispute_date == future
-        ])
-
-        self.assertEqual(0, collection.maximum_size)
-
-        collection = Transaction.search([
-            TransactionSearch.id == disputed_transaction_id,
-            TransactionSearch.dispute_date == disputed_date
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-
-    def test_advanced_search_range_node_disputed_date_with_dates(self):
-        disputed_transaction_id = TestHelper.create_disputed_transaction().id
-        disputed_date = datetime.today()
-        past = disputed_date - timedelta(days=1)
-        future = disputed_date + timedelta(days=1)
-
-        collection = Transaction.search([
-            TransactionSearch.id == disputed_transaction_id,
-            TransactionSearch.dispute_date.between(past, future)
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(disputed_transaction_id, collection.first.id)
-
-    def test_advanced_search_range_node_authorization_expired_at(self):
-        two_days_ago = datetime.today() - timedelta(days=2)
-        yesterday = datetime.today() - timedelta(days=1)
-        tomorrow = datetime.today() + timedelta(days=1)
-
-        collection = Transaction.search(
-            TransactionSearch.authorization_expired_at.between(two_days_ago, yesterday)
-        )
-        self.assertEqual(0, collection.maximum_size)
-
-        collection = Transaction.search(
-            TransactionSearch.authorization_expired_at.between(yesterday, tomorrow)
-        )
-        self.assertTrue(collection.maximum_size > 0)
-        self.assertEqual(Transaction.Status.AuthorizationExpired, collection.first.status)
-
-
-    def test_advanced_search_range_node_authorized_at(self):
-        transaction = Transaction.sale({
-             "amount": TransactionAmounts.Authorize,
-             "credit_card": {
-                 "number": "4111111111111111",
-                 "expiration_date": "05/2012"
-             }
-        }).transaction
-
-        past = datetime.today() - timedelta(days=1)
-        future = datetime.today() + timedelta(days=1)
-        future2 = datetime.today() + timedelta(days=2)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.authorized_at.between(past, future)
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(transaction.id, collection.first.id)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.authorized_at.between(future, future2)
-        ])
-
-        self.assertEqual(0, collection.maximum_size)
-
-    def test_advanced_search_range_node_failed_at(self):
-        transaction = Transaction.sale({
-             "amount": TransactionAmounts.Fail,
-             "credit_card": {
-                 "number": "4111111111111111",
-                 "expiration_date": "05/2012"
-             }
-        }).transaction
-
-        past = datetime.today() - timedelta(days=1)
-        future = datetime.today() + timedelta(days=1)
-        future2 = datetime.today() + timedelta(days=2)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.failed_at.between(past, future)
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(transaction.id, collection.first.id)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.failed_at.between(future, future2)
-        ])
-
-        self.assertEqual(0, collection.maximum_size)
-
-    def test_advanced_search_range_node_gateway_rejected_at(self):
-        old_merchant_id = Configuration.merchant_id
-        old_public_key = Configuration.public_key
-        old_private_key = Configuration.private_key
-
-        try:
-            Configuration.merchant_id = "processing_rules_merchant_id"
-            Configuration.public_key = "processing_rules_public_key"
-            Configuration.private_key = "processing_rules_private_key"
-
-            transaction = Transaction.sale({
-                 "amount": TransactionAmounts.Authorize,
-                 "credit_card": {
-                     "number": "4111111111111111",
-                     "expiration_date": "05/2012",
-                     "cvv": "200"
-                 }
-            }).transaction
-
-            past = datetime.today() - timedelta(days=1)
-            future = datetime.today() + timedelta(days=1)
-            future2 = datetime.today() + timedelta(days=2)
-
-            collection = Transaction.search([
-                TransactionSearch.id == transaction.id,
-                TransactionSearch.gateway_rejected_at.between(past, future)
-            ])
-
-            self.assertEqual(1, collection.maximum_size)
-            self.assertEqual(transaction.id, collection.first.id)
-
-            collection = Transaction.search([
-                TransactionSearch.id == transaction.id,
-                TransactionSearch.gateway_rejected_at.between(future, future2)
-            ])
-
-            self.assertEqual(0, collection.maximum_size)
-        finally:
-            Configuration.merchant_id = old_merchant_id
-            Configuration.public_key = old_public_key
-            Configuration.private_key = old_private_key
-
-    def test_advanced_search_range_node_processor_declined_at(self):
-        transaction = Transaction.sale({
-             "amount": TransactionAmounts.Decline,
-             "credit_card": {
-                 "number": "4111111111111111",
-                 "expiration_date": "05/2012"
-             }
-        }).transaction
-
-        past = datetime.today() - timedelta(days=1)
-        future = datetime.today() + timedelta(days=1)
-        future2 = datetime.today() + timedelta(days=2)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.processor_declined_at.between(past, future)
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(transaction.id, collection.first.id)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.processor_declined_at.between(future, future2)
-        ])
-
-        self.assertEqual(0, collection.maximum_size)
-
-    def test_advanced_search_range_node_settled_at(self):
-        transaction = Transaction.sale({
-             "amount": TransactionAmounts.Authorize,
-             "credit_card": {
-                 "number": "4111111111111111",
-                 "expiration_date": "05/2012"
-             },
-             "options": {
-                 "submit_for_settlement": True
-             }
-        }).transaction
-
-        TestHelper.settle_transaction(transaction.id)
-        transaction = Transaction.find(transaction.id)
-
-        past = datetime.today() - timedelta(days=1)
-        future = datetime.today() + timedelta(days=1)
-        future2 = datetime.today() + timedelta(days=2)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.settled_at.between(past, future)
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(transaction.id, collection.first.id)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.settled_at.between(future, future2)
-        ])
-
-        self.assertEqual(0, collection.maximum_size)
-
-    def test_advanced_search_range_node_submitted_for_settlement_at(self):
-        transaction = Transaction.sale({
-             "amount": TransactionAmounts.Authorize,
-             "credit_card": {
-                 "number": "4111111111111111",
-                 "expiration_date": "05/2012"
-             },
-             "options": {
-                 "submit_for_settlement": True
-             }
-        }).transaction
-
-        past = datetime.today() - timedelta(days=1)
-        future = datetime.today() + timedelta(days=1)
-        future2 = datetime.today() + timedelta(days=2)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.submitted_for_settlement_at.between(past, future)
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(transaction.id, collection.first.id)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.submitted_for_settlement_at.between(future, future2)
-        ])
-
-        self.assertEqual(0, collection.maximum_size)
-
-    def test_advanced_search_range_node_voided_at(self):
-        transaction = Transaction.sale({
-             "amount": TransactionAmounts.Authorize,
-             "credit_card": {
-                 "number": "4111111111111111",
-                 "expiration_date": "05/2012"
-             }
-        }).transaction
-        transaction = Transaction.void(transaction.id).transaction
-
-        past = datetime.today() - timedelta(days=1)
-        future = datetime.today() + timedelta(days=1)
-        future2 = datetime.today() + timedelta(days=2)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.voided_at.between(past, future)
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(transaction.id, collection.first.id)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.voided_at.between(future, future2)
-        ])
-
-        self.assertEqual(0, collection.maximum_size)
-
-    def test_advanced_search_range_node_can_search_on_multiple_statuses(self):
-        transaction = Transaction.sale({
-             "amount": TransactionAmounts.Authorize,
-             "credit_card": {
-                 "number": "4111111111111111",
-                 "expiration_date": "05/2012"
-             },
-             "options": {
-                 "submit_for_settlement": True
-             }
-        }).transaction
-
-        past = datetime.today() - timedelta(days=1)
-        future = datetime.today() + timedelta(days=1)
-        future2 = datetime.today() + timedelta(days=2)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.authorized_at.between(past, future),
-            TransactionSearch.submitted_for_settlement_at.between(past, future)
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(transaction.id, collection.first.id)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.authorized_at.between(future, future2),
-            TransactionSearch.submitted_for_settlement_at.between(future, future2)
-        ])
-
-        self.assertEqual(0, collection.maximum_size)
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.authorized_at.between(past, future),
-            TransactionSearch.voided_at.between(past, future)
-        ])
-
-        self.assertEqual(0, collection.maximum_size)
-
-    def test_advanced_search_returns_iteratable_results(self):
-        collection = Transaction.search([
-            TransactionSearch.credit_card_number.starts_with("411")
-        ])
-
-        self.assertTrue(collection.maximum_size > 100)
-
-        transaction_ids = [transaction.id for transaction in collection.items]
-        self.assertEqual(collection.maximum_size, len(TestHelper.unique(transaction_ids)))
-
-    def test_advanced_search_can_search_on_paypal_fields(self):
-        http = ClientApiHttp.create()
-        status_code, nonce = http.get_paypal_nonce({
-            "access_token": "PAYPAL-ACCESS-TOKEN",
-            "options": {"validate": False}
-        })
-        self.assertEqual(202, status_code)
-
-        transaction = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "payment_method_nonce": nonce
-        }).transaction
-
-        collection = Transaction.search([
-            TransactionSearch.paypal_payer_email == transaction.paypal_details.payer_email,
-            TransactionSearch.paypal_authorization_id == transaction.paypal_details.authorization_id,
-            TransactionSearch.paypal_payment_id == transaction.paypal_details.payment_id,
-        ])
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(transaction.id, collection.first.id)
-
-    @raises(DownForMaintenanceError)
-    def test_search_handles_a_search_timeout(self):
-        Transaction.search([
-            TransactionSearch.amount.between("-1100", "1600")
-        ])
diff --git a/tests/integration/test_transaction_with_us_bank_account.py b/tests/integration/test_transaction_with_us_bank_account.py
deleted file mode 100644
index 571b66c..0000000
--- a/tests/integration/test_transaction_with_us_bank_account.py
+++ /dev/null
@@ -1,131 +0,0 @@
-from tests.test_helper import *
-from braintree.payment_instrument_type import PaymentInstrumentType
-from braintree.us_bank_account_verification import UsBankAccountVerification
-
-class TestTransactionWithUsBankAccount(unittest.TestCase):
-    def test_nonce_transactions(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "merchant_account_id": TestHelper.us_bank_merchant_account_id,
-            "payment_method_nonce": TestHelper.generate_valid_us_bank_account_nonce(),
-            "options": {
-                "submit_for_settlement": True,
-                "store_in_vault": True
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertEqual(result.transaction.us_bank_account.routing_number, "021000021")
-        self.assertEqual(result.transaction.us_bank_account.last_4, "1234")
-        self.assertEqual(result.transaction.us_bank_account.account_type, "checking")
-        self.assertEqual(result.transaction.us_bank_account.account_holder_name, "Dan Schulman")
-        self.assertTrue(re.match(r".*CHASE.*", result.transaction.us_bank_account.bank_name))
-        self.assertEqual(result.transaction.us_bank_account.ach_mandate.text, "cl mandate text")
-        self.assertIsInstance(result.transaction.us_bank_account.ach_mandate.accepted_at, datetime)
-
-    def test_nonce_transactions_with_vaulted_token(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "merchant_account_id": TestHelper.us_bank_merchant_account_id,
-            "payment_method_nonce": TestHelper.generate_valid_us_bank_account_nonce(),
-            "options": {
-                "submit_for_settlement": True,
-                "store_in_vault": True
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertEqual(result.transaction.us_bank_account.routing_number, "021000021")
-        self.assertEqual(result.transaction.us_bank_account.last_4, "1234")
-        self.assertEqual(result.transaction.us_bank_account.account_type, "checking")
-        self.assertEqual(result.transaction.us_bank_account.account_holder_name, "Dan Schulman")
-        self.assertTrue(re.match(r".*CHASE.*", result.transaction.us_bank_account.bank_name))
-        self.assertEqual(result.transaction.us_bank_account.ach_mandate.text, "cl mandate text")
-        self.assertIsInstance(result.transaction.us_bank_account.ach_mandate.accepted_at, datetime)
-        token = result.transaction.us_bank_account.token
-
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "merchant_account_id": TestHelper.us_bank_merchant_account_id,
-            "payment_method_token": token,
-            "options": {
-                "submit_for_settlement": True,
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertEqual(result.transaction.us_bank_account.routing_number, "021000021")
-        self.assertEqual(result.transaction.us_bank_account.last_4, "1234")
-        self.assertEqual(result.transaction.us_bank_account.account_type, "checking")
-        self.assertEqual(result.transaction.us_bank_account.account_holder_name, "Dan Schulman")
-        self.assertTrue(re.match(r".*CHASE.*", result.transaction.us_bank_account.bank_name))
-        self.assertEqual(result.transaction.us_bank_account.ach_mandate.text, "cl mandate text")
-        self.assertIsInstance(result.transaction.us_bank_account.ach_mandate.accepted_at, datetime)
-
-    def test_token_transactions_not_found(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "merchant_account_id": TestHelper.us_bank_merchant_account_id,
-            "payment_method_nonce": TestHelper.generate_invalid_us_bank_account_nonce(),
-            "options": {
-                "submit_for_settlement": True,
-                "store_in_vault": True
-            }
-        })
-
-        self.assertFalse(result.is_success)
-        error_code = result.errors.for_object("transaction").on("payment_method_nonce")[0].code
-        self.assertEqual(error_code, ErrorCodes.Transaction.PaymentMethodNonceUnknown)
-
-    def test_verification_create_with_plaid_nonce(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "merchant_account_id": TestHelper.us_bank_merchant_account_id,
-            "payment_method_nonce": TestHelper.generate_plaid_us_bank_account_nonce(),
-            "options": {
-                "submit_for_settlement": True,
-                "store_in_vault": True
-            }
-        })
-
-        self.assertTrue(result.is_success)
-
-        token = result.transaction.us_bank_account.token
-        us_bank_account = PaymentMethod.find(token)
-
-        self.assertEqual(result.transaction.payment_instrument_type, PaymentInstrumentType.UsBankAccount)
-        self.assertEqual(len(us_bank_account.verifications), 1)
-        self.assertEqual(us_bank_account.verifications[0].verification_method, UsBankAccountVerification.VerificationMethod.TokenizedCheck)
-        self.assertEqual(us_bank_account.verifications[0].status, UsBankAccountVerification.Status.Verified)
-
-class TestTransactionWithUsBankAccountCompliantMerchant(unittest.TestCase):
-    def setUp(self):
-        braintree.Configuration.configure(
-            braintree.Environment.Development,
-            "integration2_merchant_id",
-            "integration2_public_key",
-            "integration2_private_key"
-        )
-
-    def tearDown(self):
-        braintree.Configuration.configure(
-            braintree.Environment.Development,
-            "integration_merchant_id",
-            "integration_public_key",
-            "integration_private_key"
-        )
-
-    def test_reject_non_plaid_nonce(self):
-        result = Transaction.sale({
-            "amount": TransactionAmounts.Authorize,
-            "merchant_account_id": "another_us_bank_merchant_account",
-            "payment_method_nonce": TestHelper.generate_valid_us_bank_account_nonce(),
-            "options": {
-                "submit_for_settlement": True,
-                "store_in_vault": True
-            }
-        })
-
-        self.assertFalse(result.is_success)
-        error_code = result.errors.for_object("transaction").on("payment_method_nonce")[0].code
-        self.assertEqual(ErrorCodes.Transaction.UsBankAccountNonceMustBePlaidVerified, error_code)
diff --git a/tests/integration/test_transparent_redirect.py b/tests/integration/test_transparent_redirect.py
deleted file mode 100644
index e336d07..0000000
--- a/tests/integration/test_transparent_redirect.py
+++ /dev/null
@@ -1,186 +0,0 @@
-from tests.test_helper import *
-
-class TestTransparentRedirect(unittest.TestCase):
-    @raises(DownForMaintenanceError)
-    def test_parse_and_validate_query_string_checks_http_status_before_hash(self):
-        customer = Customer.create().customer
-        tr_data = {
-            "credit_card": {
-                "customer_id": customer.id
-            }
-        }
-        post_params = {
-            "tr_data": CreditCard.tr_data_for_create(tr_data, "http://example.com/path?foo=bar"),
-            "credit_card[cardholder_name]": "Card Holder",
-            "credit_card[number]": "4111111111111111",
-            "credit_card[expiration_date]": "05/2012",
-        }
-
-        query_string = TestHelper.simulate_tr_form_post(post_params, Configuration.instantiate().base_merchant_path() + "/test/maintenance")
-        CreditCard.confirm_transparent_redirect(query_string)
-
-    @raises(AuthenticationError)
-    def test_parse_and_validate_query_string_raises_authentication_error_with_bad_credentials(self):
-        customer = Customer.create().customer
-        tr_data = {
-            "credit_card": {
-                "customer_id": customer.id
-            }
-        }
-
-        old_private_key = Configuration.private_key
-        try:
-            Configuration.private_key = "bad"
-
-            post_params = {
-                "tr_data": CreditCard.tr_data_for_create(tr_data, "http://example.com/path?foo=bar"),
-                "credit_card[cardholder_name]": "Card Holder",
-                "credit_card[number]": "4111111111111111",
-                "credit_card[expiration_date]": "05/2012",
-            }
-            query_string = TestHelper.simulate_tr_form_post(post_params, CreditCard.transparent_redirect_create_url())
-            CreditCard.confirm_transparent_redirect(query_string)
-        finally:
-            Configuration.private_key = old_private_key
-
-    def test_transaction_sale_from_transparent_redirect_with_successful_result(self):
-        tr_data = {
-            "transaction": {
-                "amount": TransactionAmounts.Authorize,
-            }
-        }
-        post_params = {
-            "tr_data": Transaction.tr_data_for_sale(tr_data, "http://example.com/path"),
-            "transaction[credit_card][number]": "4111111111111111",
-            "transaction[credit_card][expiration_date]": "05/2010",
-        }
-
-        query_string = TestHelper.simulate_tr_form_post(post_params)
-        result = TransparentRedirect.confirm(query_string)
-        self.assertTrue(result.is_success)
-
-        transaction = result.transaction
-        self.assertEqual(Decimal(TransactionAmounts.Authorize), transaction.amount)
-        self.assertEqual(Transaction.Type.Sale, transaction.type)
-        self.assertEqual("411111", transaction.credit_card_details.bin)
-        self.assertEqual("1111", transaction.credit_card_details.last_4)
-        self.assertEqual("05/2010", transaction.credit_card_details.expiration_date)
-
-    def test_transaction_credit_from_transparent_redirect_with_successful_result(self):
-        tr_data = {
-            "transaction": {
-                "amount": TransactionAmounts.Authorize,
-            }
-        }
-        post_params = {
-            "tr_data": Transaction.tr_data_for_credit(tr_data, "http://example.com/path"),
-            "transaction[credit_card][number]": "4111111111111111",
-            "transaction[credit_card][expiration_date]": "05/2010",
-        }
-
-        query_string = TestHelper.simulate_tr_form_post(post_params)
-        result = TransparentRedirect.confirm(query_string)
-        self.assertTrue(result.is_success)
-
-        transaction = result.transaction
-        self.assertEqual(Decimal(TransactionAmounts.Authorize), transaction.amount)
-        self.assertEqual(Transaction.Type.Credit, transaction.type)
-        self.assertEqual("411111", transaction.credit_card_details.bin)
-        self.assertEqual("1111", transaction.credit_card_details.last_4)
-        self.assertEqual("05/2010", transaction.credit_card_details.expiration_date)
-
-    def test_customer_create_from_transparent_redirect(self):
-        tr_data = {
-            "customer": {
-                "first_name": "John",
-                "last_name": "Doe",
-                "company": "Doe Co",
-            }
-        }
-        post_params = {
-            "tr_data": Customer.tr_data_for_create(tr_data, "http://example.com/path"),
-            "customer[email]": "john@doe.com",
-            "customer[phone]": "312.555.2323",
-            "customer[fax]": "614.555.5656",
-            "customer[website]": "www.johndoe.com"
-        }
-
-        query_string = TestHelper.simulate_tr_form_post(post_params)
-        result = TransparentRedirect.confirm(query_string)
-        self.assertTrue(result.is_success)
-        customer = result.customer
-        self.assertEqual("John", customer.first_name)
-        self.assertEqual("Doe", customer.last_name)
-        self.assertEqual("Doe Co", customer.company)
-        self.assertEqual("john@doe.com", customer.email)
-        self.assertEqual("312.555.2323", customer.phone)
-        self.assertEqual("614.555.5656", customer.fax)
-        self.assertEqual("www.johndoe.com", customer.website)
-
-    def test_customer_update_from_transparent_redirect(self):
-        customer = Customer.create({"first_name": "Sarah", "last_name": "Humphrey"}).customer
-
-        tr_data = {
-            "customer_id": customer.id,
-            "customer": {
-                "first_name": "Stan",
-            }
-        }
-        post_params = {
-            "tr_data": Customer.tr_data_for_update(tr_data, "http://example.com/path"),
-            "customer[last_name]": "Humphrey",
-        }
-
-        query_string = TestHelper.simulate_tr_form_post(post_params)
-        result = TransparentRedirect.confirm(query_string)
-        self.assertTrue(result.is_success)
-
-        customer = Customer.find(customer.id)
-        self.assertEqual("Stan", customer.first_name)
-        self.assertEqual("Humphrey", customer.last_name)
-
-    def test_payment_method_create_from_transparent_redirect(self):
-        customer = Customer.create({"first_name": "Sarah", "last_name": "Humphrey"}).customer
-        tr_data = {
-            "credit_card": {
-                "customer_id": customer.id,
-                "number": "4111111111111111",
-            }
-        }
-        post_params = {
-            "tr_data": CreditCard.tr_data_for_create(tr_data, "http://example.com/path"),
-            "credit_card[expiration_month]": "01",
-            "credit_card[expiration_year]": "10"
-        }
-
-        query_string = TestHelper.simulate_tr_form_post(post_params)
-        result = TransparentRedirect.confirm(query_string)
-        self.assertTrue(result.is_success)
-        credit_card = result.credit_card
-        self.assertEqual("411111", credit_card.bin)
-        self.assertEqual("1111", credit_card.last_4)
-        self.assertEqual("01/2010", credit_card.expiration_date)
-
-    def test_payment_method_update_from_transparent_redirect(self):
-        customer = Customer.create({"first_name": "Sarah", "last_name": "Humphrey"}).customer
-        credit_card = CreditCard.create({
-            "customer_id": customer.id,
-            "number": "4111111111111111",
-            "expiration_date": "10/10"
-        }).credit_card
-
-        tr_data = {
-            "payment_method_token": credit_card.token,
-            "credit_card": {
-                "expiration_date": "12/12"
-            }
-        }
-        post_params = {
-            "tr_data": CreditCard.tr_data_for_update(tr_data, "http://example.com/path"),
-        }
-
-        query_string = TestHelper.simulate_tr_form_post(post_params)
-        TransparentRedirect.confirm(query_string)
-        credit_card = CreditCard.find(credit_card.token)
-
-        self.assertEqual("12/2012", credit_card.expiration_date)
diff --git a/tests/integration/test_us_bank_account.py b/tests/integration/test_us_bank_account.py
deleted file mode 100644
index b1e4a9c..0000000
--- a/tests/integration/test_us_bank_account.py
+++ /dev/null
@@ -1,53 +0,0 @@
-from tests.test_helper import *
-
-class TestUsBankAccount(unittest.TestCase):
-    def test_find_returns_us_bank_account(self):
-        customer_id = Customer.create().customer.id
-        result = PaymentMethod.create({
-            "customer_id": customer_id,
-            "payment_method_nonce": TestHelper.generate_valid_us_bank_account_nonce(),
-            "options": {
-                "verification_merchant_account_id": TestHelper.us_bank_merchant_account_id
-            }
-        })
-        self.assertTrue(result.is_success)
-
-        found_account = UsBankAccount.find(result.payment_method.token)
-        self.assertEqual(found_account.routing_number, "021000021")
-        self.assertEqual(found_account.last_4, "1234")
-        self.assertEqual(found_account.account_type, "checking")
-        self.assertEqual(found_account.account_holder_name, "Dan Schulman")
-        self.assertRegexpMatches(found_account.bank_name, r".*CHASE.*")
-        self.assertEqual(found_account.default, True)
-        self.assertEqual(found_account.ach_mandate.text, "cl mandate text")
-        self.assertIsNotNone(found_account.ach_mandate.accepted_at)
-        self.assertIsInstance(found_account.ach_mandate.accepted_at, datetime)
-
-    def test_find_does_not_return_invalid_us_bank_account(self):
-        self.assertRaises(NotFoundError, UsBankAccount.find, TestHelper.generate_invalid_us_bank_account_nonce())
-
-    def test_sale_transacts_us_bank_account(self):
-        customer_id = Customer.create().customer.id
-        result = PaymentMethod.create({
-            "customer_id": customer_id,
-            "payment_method_nonce": TestHelper.generate_valid_us_bank_account_nonce(),
-            "options": {
-                "verification_merchant_account_id": TestHelper.us_bank_merchant_account_id
-            }
-        })
-        self.assertTrue(result.is_success)
-
-        params = {
-            "amount": TransactionAmounts.Authorize,
-            "merchant_account_id": TestHelper.us_bank_merchant_account_id,
-        }
-        result = UsBankAccount.sale(result.payment_method.token, params)
-
-        self.assertTrue(result.is_success)
-        self.assertEqual(result.transaction.us_bank_account.routing_number, "021000021")
-        self.assertEqual(result.transaction.us_bank_account.last_4, "1234")
-        self.assertEqual(result.transaction.us_bank_account.account_type, "checking")
-        self.assertEqual(result.transaction.us_bank_account.account_holder_name, "Dan Schulman")
-        self.assertTrue(re.match(r".*CHASE.*", result.transaction.us_bank_account.bank_name))
-
-
diff --git a/tests/integration/test_us_bank_account_verification.py b/tests/integration/test_us_bank_account_verification.py
deleted file mode 100644
index 306c053..0000000
--- a/tests/integration/test_us_bank_account_verification.py
+++ /dev/null
@@ -1,230 +0,0 @@
-from tests.test_helper import *
-
-from braintree.us_bank_account_verification import UsBankAccountVerification
-from braintree.us_bank_account_verification_search import UsBankAccountVerificationSearch
-
-class TestUsBankAccountVerification(unittest.TestCase):
-    def test_find_by_id(self):
-        customer_id = Customer.create().customer.id
-        result = PaymentMethod.create({
-            "customer_id": customer_id,
-            "payment_method_nonce": TestHelper.generate_valid_us_bank_account_nonce(),
-            "options": {
-                "verification_merchant_account_id": TestHelper.us_bank_merchant_account_id,
-                "us_bank_account_verification_method": UsBankAccountVerification.VerificationMethod.IndependentCheck
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertEqual(len(result.payment_method.verifications), 1)
-
-        created_verification = result.payment_method.verifications[0]
-
-        found_verification = UsBankAccountVerification.find(created_verification.id)
-
-        self.assertEqual(created_verification, found_verification)
-
-    def test_search_by_verification_method(self):
-        customer_id = Customer.create().customer.id
-        result = PaymentMethod.create({
-            "customer_id": customer_id,
-            "payment_method_nonce": TestHelper.generate_valid_us_bank_account_nonce(),
-            "options": {
-                "verification_merchant_account_id": TestHelper.us_bank_merchant_account_id,
-                "us_bank_account_verification_method": UsBankAccountVerification.VerificationMethod.IndependentCheck
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertEqual(len(result.payment_method.verifications), 1)
-
-        created_verification = result.payment_method.verifications[0]
-
-        found_verifications = UsBankAccountVerification.search(
-            UsBankAccountVerificationSearch.verification_method.in_list(
-                [UsBankAccountVerification.VerificationMethod.IndependentCheck]
-            ),
-            UsBankAccountVerificationSearch.customer_id == customer_id,
-        )
-
-        self.assertEqual(1, found_verifications.maximum_size)
-        self.assertEqual(created_verification, found_verifications.first)
-
-    def test_search_by_status(self):
-        customer_id = Customer.create().customer.id
-        result = PaymentMethod.create({
-            "customer_id": customer_id,
-            "payment_method_nonce": TestHelper.generate_valid_us_bank_account_nonce(),
-            "options": {
-                "verification_merchant_account_id": TestHelper.us_bank_merchant_account_id,
-                "us_bank_account_verification_method": UsBankAccountVerification.VerificationMethod.IndependentCheck
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertEqual(len(result.payment_method.verifications), 1)
-
-        created_verification = result.payment_method.verifications[0]
-
-        found_verifications = UsBankAccountVerification.search(
-            UsBankAccountVerificationSearch.status.in_list([UsBankAccountVerification.Status.Verified]),
-            UsBankAccountVerificationSearch.customer_id == customer_id,
-        )
-
-        self.assertEqual(1, found_verifications.maximum_size)
-        self.assertEqual(created_verification, found_verifications.first)
-
-    def test_search_by_account_number(self):
-        customer_id = Customer.create().customer.id
-        result = PaymentMethod.create({
-            "customer_id": customer_id,
-            "payment_method_nonce": TestHelper.generate_valid_us_bank_account_nonce(account_number="1000000000"),
-            "options": {
-                "verification_merchant_account_id": TestHelper.us_bank_merchant_account_id,
-                "us_bank_account_verification_method": UsBankAccountVerification.VerificationMethod.IndependentCheck
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertEqual(len(result.payment_method.verifications), 1)
-
-        created_verification = result.payment_method.verifications[0]
-
-        found_verifications = UsBankAccountVerification.search(
-            UsBankAccountVerificationSearch.account_number.ends_with("0000"),
-            UsBankAccountVerificationSearch.customer_id == customer_id,
-        )
-
-        self.assertEqual(1, found_verifications.maximum_size)
-        self.assertEqual(created_verification, found_verifications.first)
-
-class TestUsBankAccountVerificationCompliant(unittest.TestCase):
-    def setUp(self):
-        braintree.Configuration.configure(
-            braintree.Environment.Development,
-            "integration2_merchant_id",
-            "integration2_public_key",
-            "integration2_private_key"
-        )
-
-    def tearDown(self):
-        braintree.Configuration.configure(
-            braintree.Environment.Development,
-            "integration_merchant_id",
-            "integration_public_key",
-            "integration_private_key"
-        )
-
-    def test_successfully_confirm_settled_micro_transfer_amounts(self):
-        customer_id = Customer.create().customer.id
-        result = PaymentMethod.create({
-            "customer_id": customer_id,
-            "payment_method_nonce": TestHelper.generate_valid_us_bank_account_nonce(account_number="1000000000"),
-            "options": {
-                "verification_merchant_account_id": TestHelper.another_us_bank_merchant_account_id,
-                "us_bank_account_verification_method": UsBankAccountVerification.VerificationMethod.MicroTransfers
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertEqual(len(result.payment_method.verifications), 1)
-
-        verification = result.payment_method.verifications[0]
-
-        self.assertEqual(verification.status, UsBankAccountVerification.Status.Pending)
-        self.assertEqual(verification.verification_method, UsBankAccountVerification.VerificationMethod.MicroTransfers)
-
-        result = UsBankAccountVerification.confirm_micro_transfer_amounts(verification.id, [17, 29])
-
-        self.assertTrue(result.is_success)
-
-        self.assertEqual(result.us_bank_account_verification.status, UsBankAccountVerification.Status.Verified)
-
-        us_bank_account = UsBankAccount.find(result.us_bank_account_verification.us_bank_account.token)
-
-        self.assertTrue(us_bank_account.verified)
-
-    def test_successfully_confirm_unsettled_micro_transfer_amounts(self):
-        customer_id = Customer.create().customer.id
-        result = PaymentMethod.create({
-            "customer_id": customer_id,
-            "payment_method_nonce": TestHelper.generate_valid_us_bank_account_nonce(account_number="1000000001"),
-            "options": {
-                "verification_merchant_account_id": TestHelper.another_us_bank_merchant_account_id,
-                "us_bank_account_verification_method": UsBankAccountVerification.VerificationMethod.MicroTransfers
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertEqual(len(result.payment_method.verifications), 1)
-
-        verification = result.payment_method.verifications[0]
-
-        self.assertEqual(verification.status, UsBankAccountVerification.Status.Pending)
-        self.assertEqual(verification.verification_method, UsBankAccountVerification.VerificationMethod.MicroTransfers)
-
-        result = UsBankAccountVerification.confirm_micro_transfer_amounts(verification.id, [17, 29])
-
-        self.assertTrue(result.is_success)
-
-        self.assertEqual(result.us_bank_account_verification.status, UsBankAccountVerification.Status.Pending)
-
-        us_bank_account = UsBankAccount.find(result.us_bank_account_verification.us_bank_account.token)
-
-        self.assertFalse(us_bank_account.verified)
-
-    def test_attempt_confirm_micro_transfer_amounts(self):
-        customer_id = Customer.create().customer.id
-        result = PaymentMethod.create({
-            "customer_id": customer_id,
-            "payment_method_nonce": TestHelper.generate_valid_us_bank_account_nonce(account_number="1000000000"),
-            "options": {
-                "verification_merchant_account_id": TestHelper.another_us_bank_merchant_account_id,
-                "us_bank_account_verification_method": UsBankAccountVerification.VerificationMethod.MicroTransfers
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertEqual(len(result.payment_method.verifications), 1)
-
-        verification = result.payment_method.verifications[0]
-
-        self.assertEqual(verification.status, UsBankAccountVerification.Status.Pending)
-        self.assertEqual(verification.verification_method, UsBankAccountVerification.VerificationMethod.MicroTransfers)
-
-        result = UsBankAccountVerification.confirm_micro_transfer_amounts(verification.id, [1, 1])
-
-        self.assertFalse(result.is_success)
-
-        error_code = result.errors.for_object("us_bank_account_verification").on("base")[0].code
-        self.assertEqual(ErrorCodes.UsBankAccountVerification.AmountsDoNotMatch, error_code)
-
-    def test_gateway_reject_confirm_micro_transfer_amounts(self):
-        customer_id = Customer.create().customer.id
-        result = PaymentMethod.create({
-            "customer_id": customer_id,
-            "payment_method_nonce": TestHelper.generate_valid_us_bank_account_nonce(account_number="1000000000"),
-            "options": {
-                "verification_merchant_account_id": TestHelper.another_us_bank_merchant_account_id,
-                "us_bank_account_verification_method": UsBankAccountVerification.VerificationMethod.MicroTransfers
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        self.assertEqual(len(result.payment_method.verifications), 1)
-
-        verification = result.payment_method.verifications[0]
-
-        self.assertEqual(verification.status, UsBankAccountVerification.Status.Pending)
-        self.assertEqual(verification.verification_method, UsBankAccountVerification.VerificationMethod.MicroTransfers)
-
-        for i in range(4):
-            result = UsBankAccountVerification.confirm_micro_transfer_amounts(verification.id, [1, 1])
-            self.assertFalse(result.is_success)
-            error_code = result.errors.for_object("us_bank_account_verification").on("base")[0].code
-            self.assertEqual(ErrorCodes.UsBankAccountVerification.AmountsDoNotMatch, error_code)
-
-        result = UsBankAccountVerification.confirm_micro_transfer_amounts(verification.id, [1, 1])
-        self.assertFalse(result.is_success)
-        error_code = result.errors.for_object("us_bank_account_verification").on("base")[0].code
-        self.assertEqual(ErrorCodes.UsBankAccountVerification.TooManyConfirmationAttempts, error_code)
diff --git a/tests/integration/test_visa_checkout.py b/tests/integration/test_visa_checkout.py
deleted file mode 100644
index 1aefb02..0000000
--- a/tests/integration/test_visa_checkout.py
+++ /dev/null
@@ -1,112 +0,0 @@
-from tests.test_helper import *
-
-class TestVisaCheckout(unittest.TestCase):
-    def test_create_from_nonce(self):
-        customer = Customer.create().customer
-        result = PaymentMethod.create({
-            "customer_id": customer.id,
-            "payment_method_nonce": Nonces.VisaCheckoutVisa
-        })
-
-        self.assertTrue(result.is_success)
-
-        visa_checkout_card = result.payment_method
-        self.assertEqual("abc123", visa_checkout_card.call_id)
-        self.assertIsNotNone(visa_checkout_card.billing_address)
-        self.assertIsNotNone(visa_checkout_card.bin)
-        self.assertIsNotNone(visa_checkout_card.card_type)
-        self.assertIsNotNone(visa_checkout_card.cardholder_name)
-        self.assertIsNotNone(visa_checkout_card.commercial)
-        self.assertIsNotNone(visa_checkout_card.country_of_issuance)
-        self.assertIsNotNone(visa_checkout_card.created_at)
-        self.assertIsNotNone(visa_checkout_card.customer_id)
-        self.assertIsNotNone(visa_checkout_card.customer_location)
-        self.assertIsNotNone(visa_checkout_card.debit)
-        self.assertIsNotNone(visa_checkout_card.default)
-        self.assertIsNotNone(visa_checkout_card.durbin_regulated)
-        self.assertIsNotNone(visa_checkout_card.expiration_date)
-        self.assertIsNotNone(visa_checkout_card.expiration_month)
-        self.assertIsNotNone(visa_checkout_card.expiration_year)
-        self.assertIsNotNone(visa_checkout_card.expired)
-        self.assertIsNotNone(visa_checkout_card.healthcare)
-        self.assertIsNotNone(visa_checkout_card.image_url)
-        self.assertIsNotNone(visa_checkout_card.issuing_bank)
-        self.assertIsNotNone(visa_checkout_card.last_4)
-        self.assertIsNotNone(visa_checkout_card.masked_number)
-        self.assertIsNotNone(visa_checkout_card.payroll)
-        self.assertIsNotNone(visa_checkout_card.prepaid)
-        self.assertIsNotNone(visa_checkout_card.product_id)
-        self.assertIsNotNone(visa_checkout_card.subscriptions)
-        self.assertIsNotNone(visa_checkout_card.token)
-        self.assertIsNotNone(visa_checkout_card.unique_number_identifier)
-        self.assertIsNotNone(visa_checkout_card.updated_at)
-
-        customer = Customer.find(customer.id)
-        self.assertEqual(len(customer.visa_checkout_cards), 1)
-        self.assertEqual(result.payment_method.token, customer.visa_checkout_cards[0].token)
-
-    def test_create_with_verification(self):
-        customer = Customer.create().customer
-        result = PaymentMethod.create({
-            "customer_id": customer.id,
-            "payment_method_nonce": Nonces.VisaCheckoutVisa,
-            "options": {
-                "verify_card": "true"
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        verification = result.payment_method.verification
-        self.assertEqual(CreditCardVerification.Status.Verified, verification.status)
-
-
-    def test_search_for_transaction(self):
-        result = Transaction.sale({
-            "payment_method_nonce": Nonces.VisaCheckoutVisa,
-            "amount": "1.23"
-        })
-
-        self.assertTrue(result.is_success)
-        transaction = result.transaction
-
-        collection = Transaction.search([
-            TransactionSearch.id == transaction.id,
-            TransactionSearch.payment_instrument_type == PaymentInstrumentType.VisaCheckoutCard
-        ])
-
-        self.assertEqual(1, collection.maximum_size)
-        self.assertEqual(transaction.id, collection.first.id)
-
-    def test_create_transaction_from_nonce_and_vault(self):
-        customer = Customer.create().customer
-        result = Transaction.sale({
-            "payment_method_nonce": Nonces.VisaCheckoutVisa,
-            "customer_id": customer.id,
-            "amount": "1.23",
-            "options": {
-                "store_in_vault": "true"
-            }
-        })
-
-        self.assertTrue(result.is_success)
-        visa_checkout_card_details = result.transaction.visa_checkout_card_details
-
-        self.assertEqual("abc123", visa_checkout_card_details.call_id)
-        self.assertIsNotNone(visa_checkout_card_details.bin)
-        self.assertIsNotNone(visa_checkout_card_details.card_type)
-        self.assertIsNotNone(visa_checkout_card_details.cardholder_name)
-        self.assertIsNotNone(visa_checkout_card_details.commercial)
-        self.assertIsNotNone(visa_checkout_card_details.country_of_issuance)
-        self.assertIsNotNone(visa_checkout_card_details.debit)
-        self.assertIsNotNone(visa_checkout_card_details.durbin_regulated)
-        self.assertIsNotNone(visa_checkout_card_details.expiration_date)
-        self.assertIsNotNone(visa_checkout_card_details.expiration_year)
-        self.assertIsNotNone(visa_checkout_card_details.expiration_month)
-        self.assertIsNotNone(visa_checkout_card_details.healthcare)
-        self.assertIsNotNone(visa_checkout_card_details.image_url)
-        self.assertIsNotNone(visa_checkout_card_details.issuing_bank)
-        self.assertIsNotNone(visa_checkout_card_details.last_4)
-        self.assertIsNotNone(visa_checkout_card_details.payroll)
-        self.assertIsNotNone(visa_checkout_card_details.prepaid)
-        self.assertIsNotNone(visa_checkout_card_details.product_id)
-        self.assertIsNotNone(visa_checkout_card_details.token)
diff --git a/tests/test_helper.py b/tests/test_helper.py
deleted file mode 100644
index 05a4b99..0000000
--- a/tests/test_helper.py
+++ /dev/null
@@ -1,538 +0,0 @@
-import json
-import os
-import re
-import random
-import sys
-import unittest
-import warnings
-import subprocess
-import time
-
-if sys.version_info[0] == 2:
-    from urllib import urlencode, quote_plus
-    from httplib import HTTPConnection
-    from base64 import encodestring as encodebytes
-else:
-    from urllib.parse import urlencode, quote_plus
-    from http.client import HTTPConnection
-    from base64 import encodebytes
-import requests
-
-from base64 import b64decode
-from contextlib import contextmanager
-from datetime import date, datetime, timedelta
-from decimal import Decimal
-from subprocess import Popen, PIPE
-
-from nose.tools import make_decorator
-from nose.tools import raises
-
-from braintree import *
-from braintree.exceptions import *
-from braintree.test.credit_card_numbers import CreditCardNumbers
-from braintree.test.nonces import Nonces
-from braintree.testing_gateway import *
-from braintree.util import *
-
-def raises_with_regexp(expected_exception_class, regexp_to_match):
-    def decorate(func):
-        name = func.__name__
-        def generated_function(*args, **kwargs):
-            exception_string = None
-            try:
-                func(*args, **kwargs)
-            except expected_exception_class as e:
-                exception_string = str(e)
-            except:
-                raise
-
-            if exception_string is None:
-                message = "%s() did not raise %s" % (name, expected_exception_class.__name__)
-                raise AssertionError(message)
-            elif re.match(regexp_to_match, exception_string) is None:
-                message = "%s() exception message (%s) did not match (%s)" % \
-                    (name, exception_string, regexp_to_match)
-                raise AssertionError(message)
-        return make_decorator(func)(generated_function)
-    return decorate
-
-def reset_braintree_configuration():
-    Configuration.configure(
-        Environment.Development,
-        "integration_merchant_id",
-        "integration_public_key",
-        "integration_private_key"
-    )
-reset_braintree_configuration()
-
-class AdvancedFraudIntegrationMerchant:
-    def __enter__(self):
-        Configuration.configure(
-            Environment.Development,
-            "advanced_fraud_integration_merchant_id",
-            "advanced_fraud_integration_public_key",
-            "advanced_fraud_integration_private_key"
-        )
-
-    def __exit__(self, type, value, trace):
-        reset_braintree_configuration()
-
-def showwarning(*_):
-    pass
-warnings.showwarning = showwarning
-
-class TestHelper(object):
-    default_merchant_account_id = "sandbox_credit_card"
-    non_default_merchant_account_id = "sandbox_credit_card_non_default"
-    non_default_sub_merchant_account_id = "sandbox_sub_merchant_account"
-    three_d_secure_merchant_account_id = "three_d_secure_merchant_account"
-    fake_amex_direct_merchant_account_id = "fake_amex_direct_usd"
-    fake_venmo_account_merchant_account_id = "fake_first_data_venmo_account"
-    us_bank_merchant_account_id = "us_bank_merchant_account"
-    another_us_bank_merchant_account_id = "another_us_bank_merchant_account"
-    adyen_merchant_account_id = "adyen_ma"
-    hiper_brl_merchant_account_id = "hiper_brl"
-
-    add_on_discount_plan = {
-         "description": "Plan for integration tests -- with add-ons and discounts",
-         "id": "integration_plan_with_add_ons_and_discounts",
-         "price": Decimal("9.99"),
-         "trial_duration": 2,
-         "trial_duration_unit": Subscription.TrialDurationUnit.Day,
-         "trial_period": True
-    }
-
-    billing_day_of_month_plan = {
-         "description": "Plan for integration tests -- with billing day of month",
-         "id": "integration_plan_with_billing_day_of_month",
-         "billing_day_of_month": 5,
-         "price": Decimal("8.88"),
-    }
-
-    trial_plan = {
-        "description": "Plan for integration tests -- with trial",
-        "id": "integration_trial_plan",
-        "price": Decimal("43.21"),
-        "trial_period": True,
-        "trial_duration": 2,
-        "trial_duration_unit": Subscription.TrialDurationUnit.Day
-    }
-
-    trialless_plan = {
-        "description": "Plan for integration tests -- without a trial",
-        "id": "integration_trialless_plan",
-        "price": Decimal("12.34"),
-        "trial_period": False
-    }
-
-    valid_token_characters = list("bcdfghjkmnpqrstvwxyz23456789")
-    text_type = unicode if sys.version_info[0] == 2 else str
-    raw_type = str if sys.version_info[0] == 2 else bytes
-
-    @staticmethod
-    def make_past_due(subscription, number_of_days_past_due=1):
-        Configuration.gateway().testing.make_past_due(subscription.id, number_of_days_past_due)
-
-    @staticmethod
-    def escrow_transaction(transaction_id):
-        Configuration.gateway().testing.escrow_transaction(transaction_id)
-
-    @staticmethod
-    def settle_transaction(transaction_id):
-        return Configuration.gateway().testing.settle_transaction(transaction_id)
-
-    @staticmethod
-    def settlement_confirm_transaction(transaction_id):
-        return Configuration.gateway().testing.settlement_confirm_transaction(transaction_id)
-
-    @staticmethod
-    def settlement_decline_transaction(transaction_id):
-        return Configuration.gateway().testing.settlement_decline_transaction(transaction_id)
-
-    @staticmethod
-    def settlement_pending_transaction(transaction_id):
-        return Configuration.gateway().testing.settlement_pending_transaction(transaction_id)
-
-    @staticmethod
-    def simulate_tr_form_post(post_params, url=TransparentRedirect.url()):
-        form_data = urlencode(post_params)
-        conn = HTTPConnection(Configuration.environment.server_and_port)
-        conn.request("POST", url, form_data, TestHelper.__headers())
-        response = conn.getresponse()
-        query_string = response.getheader("location").split("?", 1)[1]
-        conn.close()
-        return query_string
-
-    @staticmethod
-    def create_3ds_verification(merchant_account_id, params):
-        return Configuration.gateway().testing.create_3ds_verification(merchant_account_id, params)
-
-    @staticmethod
-    @contextmanager
-    def other_merchant(merchant_id, public_key, private_key):
-        old_merchant_id = Configuration.merchant_id
-        old_public_key = Configuration.public_key
-        old_private_key = Configuration.private_key
-
-        Configuration.merchant_id = merchant_id
-        Configuration.public_key = public_key
-        Configuration.private_key = private_key
-
-        try:
-            yield
-        finally:
-            Configuration.merchant_id = old_merchant_id
-            Configuration.public_key = old_public_key
-            Configuration.private_key = old_private_key
-
-    @staticmethod
-    def includes(collection, expected):
-        for item in collection.items:
-            if item.id == expected.id:
-                return True
-        return False
-
-    @staticmethod
-    def in_list(collection, expected):
-        for item in collection:
-            if item == expected:
-                return True
-        return False
-
-    @staticmethod
-    def includes_status(collection, status):
-        for item in collection.items:
-            if item.status == status:
-                return True
-        return False
-
-    @staticmethod
-    def now_minus_offset(offset):
-        now = datetime.utcnow()
-        return (now - timedelta(hours=offset)).strftime("%Y-%m-%d")
-
-    @staticmethod
-    def unique(some_list):
-        return set(some_list)
-
-    @staticmethod
-    def __headers():
-        return {
-            "Accept": "application/xml",
-            "Content-type": "application/x-www-form-urlencoded",
-        }
-
-    @staticmethod
-    def generate_decoded_client_token(params=None):
-        client_token = None
-        if params:
-            client_token = ClientToken.generate(params)
-        else:
-            client_token = ClientToken.generate()
-
-        decoded_client_token = b64decode(client_token).decode()
-        return decoded_client_token
-
-    @staticmethod
-    def nonce_for_paypal_account(paypal_account_details):
-        client_token = json.loads(TestHelper.generate_decoded_client_token())
-        client = ClientApiHttp(Configuration.instantiate(), {
-            "authorization_fingerprint": client_token["authorizationFingerprint"]
-        })
-
-        _, nonce = client.get_paypal_nonce(paypal_account_details)
-        return nonce
-
-    @staticmethod
-    def random_token_block(x):
-        string = ""
-        for i in range(6):
-            string += random.choice(TestHelper.valid_token_characters)
-        return string
-
-    @staticmethod
-    def generate_valid_us_bank_account_nonce(routing_number="021000021", account_number="567891234"):
-        client_token = json.loads(TestHelper.generate_decoded_client_token())
-        headers = {
-            "Content-Type": "application/json",
-            "Braintree-Version": "2016-10-07",
-            "Authorization": "Bearer " + client_token["braintree_api"]["access_token"]
-        }
-        payload = {
-            "type": "us_bank_account",
-            "billing_address": {
-                "street_address": "123 Ave",
-                "region": "CA",
-                "locality": "San Francisco",
-                "postal_code": "94112"
-            },
-            "account_type": "checking",
-            "ownership_type": "personal",
-            "routing_number": routing_number,
-            "account_number": account_number,
-            "first_name": "Dan",
-            "last_name": "Schulman",
-            "ach_mandate": {
-                "text": "cl mandate text"
-            }
-        }
-        resp = requests.post(client_token["braintree_api"]["url"] + "/tokens", headers=headers, data=json.dumps(payload) )
-        respJson = json.loads(resp.text)
-        return respJson["data"]["id"]
-
-    @staticmethod
-    def generate_plaid_us_bank_account_nonce():
-        client_token = json.loads(TestHelper.generate_decoded_client_token())
-        headers = {
-            "Content-Type": "application/json",
-            "Braintree-Version": "2016-10-07",
-            "Authorization": "Bearer " + client_token["braintree_api"]["access_token"]
-        }
-        payload = {
-            "type": "plaid_public_token",
-            "public_token": "good",
-            "account_id": "plaid_account_id",
-            "ownership_type": "business",
-            "business_name": "PayPal, Inc.",
-            "billing_address": {
-                "street_address": "123 Ave",
-                "region": "CA",
-                "locality": "San Francisco",
-                "postal_code": "94112"
-            },
-            "ach_mandate": {
-                "text": "cl mandate text"
-            }
-        }
-        resp = requests.post(client_token["braintree_api"]["url"] + "/tokens", headers=headers, data=json.dumps(payload) )
-        respJson = json.loads(resp.text)
-        return respJson["data"]["id"]
-
-    @staticmethod
-    def generate_invalid_us_bank_account_nonce():
-        token = "tokenusbankacct"
-        for i in range(4):
-            token += "_" + TestHelper.random_token_block('d')
-        token += "_xxx"
-        return token
-
-    @staticmethod
-    def generate_valid_ideal_payment_id(amount=TransactionAmounts.Authorize):
-        client_token = json.loads(TestHelper.generate_decoded_client_token({
-            "merchant_account_id": "ideal_merchant_account"
-        }))
-        client = ClientApiHttp(Configuration.instantiate(), {
-            "authorization_fingerprint": client_token["authorizationFingerprint"]
-        })
-        _, configuration = client.get_configuration()
-        route_id = json.loads(configuration)["ideal"]["routeId"]
-        headers = {
-            "Content-Type": "application/json",
-            "Braintree-Version": "2015-11-01",
-            "Authorization": "Bearer " + client_token["braintree_api"]["access_token"]
-        }
-        payload = {
-            "issuer": "RABONL2U",
-            "order_id": "ABC123",
-            "amount": amount,
-            "currency": "EUR",
-            "route_id": route_id,
-            "redirect_url": "https://braintree-api.com",
-        }
-        resp = requests.post(client_token["braintree_api"]["url"] + "/ideal-payments", headers=headers, data=json.dumps(payload) )
-        respJson = json.loads(resp.text)
-        return respJson["data"]["id"]
-
-    @staticmethod
-    def generate_three_d_secure_nonce(gateway, params):
-        url = gateway.config.base_merchant_path() + "/three_d_secure/create_nonce/" + TestHelper.three_d_secure_merchant_account_id
-        response = gateway.config.http().post(url, params)
-        return response["payment_method_nonce"]["nonce"]
-
-    @staticmethod
-    def create_disputed_transaction():
-        if hasattr(TestHelper, 'disputed_transaction'):
-            return TestHelper.disputed_transaction
-
-        disputed_transaction = Transaction.sale({
-            "amount": "10.00",
-            "credit_card": {
-                "number": CreditCardNumbers.Disputes.Chargeback,
-                "expiration_date": "04/2018"
-                }
-            })
-
-        for _ in range(1, 60):
-            transactions = Transaction.search([
-                TransactionSearch.id == disputed_transaction.transaction.id,
-                TransactionSearch.dispute_date == datetime.today()
-            ])
-
-            if transactions.maximum_size == 1:
-                TestHelper.disputed_transaction = transactions.first
-                return TestHelper.disputed_transaction
-            else:
-                time.sleep(1)
-
-        raise ValueError('Disputed transaction could not be found')
-
-    @staticmethod
-    def create_grant(gateway, params):
-        config = gateway.config
-        response = config.http().post("/oauth_testing/grants", {
-            "grant": params
-        })
-
-        return response["grant"]["code"]
-
-    @staticmethod
-    def create_payment_method_grant_fixtures():
-        config = Configuration(
-            merchant_id="integration_merchant_public_id",
-            public_key="oauth_app_partner_user_public_key",
-            private_key="oauth_app_partner_user_private_key",
-            environment=Environment.Development
-        )
-
-        gateway = BraintreeGateway(config)
-        customer = gateway.customer.create().customer
-        credit_card = gateway.credit_card.create(
-            params={
-                "customer_id": customer.id,
-                "number": "4111111111111111",
-                "expiration_date": "05/2009",
-                "billing_address": {
-                    "first_name": "Jon",
-                    "last_name": "Doe",
-                    "postal_code": "95131"
-                }
-            }
-        ).credit_card
-
-        oauth_app_gateway = BraintreeGateway(
-            client_id="client_id$development$integration_client_id",
-            client_secret="client_secret$development$integration_client_secret",
-            environment=Environment.Development
-        )
-        code = TestHelper.create_grant(oauth_app_gateway, {
-            "merchant_public_id": "integration_merchant_id",
-            "scope": "grant_payment_method"
-        })
-        access_token = oauth_app_gateway.oauth.create_token_from_code({
-            "code": code
-        }).credentials.access_token
-
-        granting_gateway = BraintreeGateway(
-            access_token=access_token,
-        )
-
-        return (granting_gateway, credit_card)
-
-    @staticmethod
-    def sample_notification_from_xml(xml):
-        gateway = Configuration.gateway()
-        payload = encodebytes(xml)
-        hmac_payload = Crypto.sha1_hmac_hash(gateway.config.private_key, payload)
-        signature = "%s|%s" % (gateway.config.public_key, hmac_payload)
-        return {'bt_signature': signature, 'bt_payload': payload}
-
-class ClientApiHttp(Http):
-    def __init__(self, config, options):
-        self.config = config
-        self.options = options
-        self.http = Http(config)
-
-    @staticmethod
-    def create():
-        config = Configuration.instantiate()
-        client_token = TestHelper.generate_decoded_client_token()
-        authorization_fingerprint = json.loads(client_token)["authorizationFingerprint"]
-        return ClientApiHttp(config, {
-            "authorization_fingerprint": authorization_fingerprint,
-            "shared_customer_identifier": "fake_identifier",
-            "shared_customer_identifier_type": "testing"
-        })
-
-    def get(self, path):
-        return self._make_request("GET", path)
-
-    def post(self, path, params=None):
-        return self._make_request("POST", path, params)
-
-    def put(self, path, params=None):
-        return self._make_request("PUT", path, params)
-
-    def _make_request(self, http_verb, path, params=None):
-        http_strategy = self.config.http_strategy()
-        request_body = json.dumps(params) if params else None
-        return http_strategy.http_do(http_verb, path, self.__headers(), request_body)
-
-    def set_authorization_fingerprint(self, authorization_fingerprint):
-        self.options['authorization_fingerprint'] = authorization_fingerprint
-
-    def get_configuration(self):
-        encoded_fingerprint = quote_plus(self.options["authorization_fingerprint"])
-        url = "/merchants/%s/client_api/v1/configuration" % self.config.merchant_id
-        url += "?authorizationFingerprint=%s" % encoded_fingerprint
-        url += "&configVersion=3"
-
-        return self.get(url)
-
-    def get_cards(self):
-        encoded_fingerprint = quote_plus(self.options["authorization_fingerprint"])
-        url = "/merchants/%s/client_api/v1/payment_methods.json" % self.config.merchant_id
-        url += "?authorizationFingerprint=%s" % encoded_fingerprint
-        url += "&sharedCustomerIdentifier=%s" % self.options["shared_customer_identifier"]
-        url += "&sharedCustomerIdentifierType=%s" % self.options["shared_customer_identifier_type"]
-
-        return self.get(url)
-
-    def add_card(self, params):
-        url = "/merchants/%s/client_api/v1/payment_methods/credit_cards.json" % self.config.merchant_id
-
-        if 'authorization_fingerprint' in self.options:
-            params['authorizationFingerprint'] = self.options['authorization_fingerprint']
-
-        if 'shared_customer_identifier' in self.options:
-            params['sharedCustomerIdentifier'] = self.options['shared_customer_identifier']
-
-        if 'shared_customer_identifier_type' in self.options:
-            params['sharedCustomerIdentifierType'] = self.options['shared_customer_identifier_type']
-
-        return self.post(url, params)
-
-    def get_paypal_nonce(self, paypal_params):
-        url = "/merchants/%s/client_api/v1/payment_methods/paypal_accounts" % self.config.merchant_id
-        params = {"paypal_account": paypal_params}
-        if 'authorization_fingerprint' in self.options:
-            params['authorizationFingerprint'] = self.options['authorization_fingerprint']
-
-        status_code, response = self.post(url, params)
-
-        nonce = None
-        if status_code == 202:
-            nonce = json.loads(response)["paypalAccounts"][0]["nonce"]
-
-        return [status_code, nonce]
-
-
-    def get_credit_card_nonce(self, credit_card_params):
-        url = "/merchants/%s/client_api/v1/payment_methods/credit_cards" % self.config.merchant_id
-        params = {"credit_card": credit_card_params}
-        if 'authorization_fingerprint' in self.options:
-            params['authorizationFingerprint'] = self.options['authorization_fingerprint']
-
-        status_code, response = self.post(url, params)
-
-        nonce = None
-        if status_code in [201, 202]:
-            nonce = json.loads(response)["creditCards"][0]["nonce"]
-
-        return [status_code, nonce]
-
-    def __headers(self):
-        return {
-            "Content-type": "application/json",
-            "User-Agent": "Braintree Python " + version.Version,
-            "X-ApiVersion": Configuration.api_version()
-        }
diff --git a/tests/unit/__init__.py b/tests/unit/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/tests/unit/merchant_account/__init__.py b/tests/unit/merchant_account/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/tests/unit/merchant_account/test_address_details.py b/tests/unit/merchant_account/test_address_details.py
deleted file mode 100644
index 091f7d7..0000000
--- a/tests/unit/merchant_account/test_address_details.py
+++ /dev/null
@@ -1,16 +0,0 @@
-from tests.test_helper import *
-from braintree.merchant_account.address_details import AddressDetails
-
-class TestAddressDetails(unittest.TestCase):
-    def test_repr_has_all_fields(self):
-        details = AddressDetails({
-            "street_address": "123 First St",
-            "region": "Las Vegas",
-            "locality": "NV",
-            "postal_code": "89913"
-        })
-
-        regex = r"<AddressDetails {street_address: '123 First St', locality: 'NV', region: 'Las Vegas', postal_code: '89913'} at \w+>"
-
-        matches = re.match(regex, repr(details))
-        self.assertTrue(matches)
diff --git a/tests/unit/merchant_account/test_business_details.py b/tests/unit/merchant_account/test_business_details.py
deleted file mode 100644
index b630324..0000000
--- a/tests/unit/merchant_account/test_business_details.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from tests.test_helper import *
-from braintree.merchant_account.business_details import BusinessDetails
-
-class TestBusinessDetails(unittest.TestCase):
-    def test_repr_has_all_fields(self):
-        details = BusinessDetails({
-            "dba_name": "Bar Suenami",
-            "legal_name": "Suenami Restaurant Group",
-            "tax_id": "123001234",
-            "address": {
-                "street_address": "123 First St",
-                "region": "Las Vegas",
-                "locality": "NV",
-            }
-        })
-
-        regex = r"<BusinessDetails {dba_name: 'Bar Suenami', legal_name: 'Suenami Restaurant Group', tax_id: '123001234', address_details: <AddressDetails {street_address: '123 First St', locality: 'NV', region: 'Las Vegas'} at \w+>} at \w+>"
-
-        matches = re.match(regex, repr(details))
-        self.assertTrue(matches)
diff --git a/tests/unit/merchant_account/test_funding_details.py b/tests/unit/merchant_account/test_funding_details.py
deleted file mode 100644
index 8bf329a..0000000
--- a/tests/unit/merchant_account/test_funding_details.py
+++ /dev/null
@@ -1,17 +0,0 @@
-from tests.test_helper import *
-from braintree.merchant_account.funding_details import FundingDetails
-
-class TestFundingDetails(unittest.TestCase):
-    def test_repr_has_all_fields(self):
-        details = FundingDetails({
-            "destination": "bank",
-            "routing_number": "11112222",
-            "account_number_last_4": "3333",
-            "email": "lucyloo@work.com",
-            "mobile_phone": "9998887777"
-        })
-
-        regex = r"<FundingDetails {account_number_last_4: '3333', routing_number: '11112222', destination: 'bank', email: 'lucyloo@work.com', mobile_phone: '9998887777'} at \w+>"
-
-        matches = re.match(regex, repr(details))
-        self.assertTrue(matches)
diff --git a/tests/unit/merchant_account/test_individual_details.py b/tests/unit/merchant_account/test_individual_details.py
deleted file mode 100644
index 5b294e6..0000000
--- a/tests/unit/merchant_account/test_individual_details.py
+++ /dev/null
@@ -1,21 +0,0 @@
-from tests.test_helper import *
-from braintree.merchant_account.individual_details import IndividualDetails
-
-class TestIndividualDetails(unittest.TestCase):
-    def test_repr_has_all_fields(self):
-        details = IndividualDetails({
-            "first_name": "Sue",
-            "last_name": "Smith",
-            "email": "sue@hotmail.com",
-            "phone": "1112223333",
-            "date_of_birth": "1980-12-05",
-            "ssn_last_4": "5555",
-            "address": {
-                "street_address": "123 First St",
-            }
-        })
-
-        regex = r"<IndividualDetails {first_name: 'Sue', last_name: 'Smith', email: 'sue@hotmail.com', phone: '1112223333', date_of_birth: '1980-12-05', ssn_last_4: '5555', address_details: <AddressDetails {street_address: '123 First St'} at \w+>} at \w+>"
-
-        matches = re.match(regex, repr(details))
-        self.assertTrue(matches)
diff --git a/tests/unit/test_address.py b/tests/unit/test_address.py
deleted file mode 100644
index ebcd6a3..0000000
--- a/tests/unit/test_address.py
+++ /dev/null
@@ -1,30 +0,0 @@
-from tests.test_helper import *
-
-class TestAddress(unittest.TestCase):
-    @raises_with_regexp(KeyError, "'Invalid keys: bad_key'")
-    def test_create_raise_exception_with_bad_keys(self):
-        Address.create({"customer_id": "12345", "bad_key": "value"})
-
-    @raises_with_regexp(KeyError, "'customer_id must be provided'")
-    def test_create_raises_error_if_no_customer_id_given(self):
-        Address.create({"country_name": "United States of America"})
-
-    @raises_with_regexp(KeyError, "'customer_id contains invalid characters'")
-    def test_create_raises_key_error_if_given_invalid_customer_id(self):
-        Address.create({"customer_id": "!@#$%"})
-
-    @raises_with_regexp(KeyError, "'Invalid keys: bad_key'")
-    def test_update_raise_exception_with_bad_keys(self):
-        Address.update("customer_id", "address_id", {"bad_key": "value"})
-
-    @raises(NotFoundError)
-    def test_finding_address_with_empty_customer_id_raises_not_found_exception(self):
-        Address.find(" ", "address_id")
-
-    @raises(NotFoundError)
-    def test_finding_address_with_none_customer_id_raises_not_found_exception(self):
-        Address.find(None, "address_id")
-
-    @raises(NotFoundError)
-    def test_finding_address_with_empty_address_id_raises_not_found_exception(self):
-        Address.find("customer_id", " ")
diff --git a/tests/unit/test_authorization_adjustment.py b/tests/unit/test_authorization_adjustment.py
deleted file mode 100644
index 4645071..0000000
--- a/tests/unit/test_authorization_adjustment.py
+++ /dev/null
@@ -1,51 +0,0 @@
-from tests.test_helper import *
-from datetime import datetime
-from braintree.authorization_adjustment import AuthorizationAdjustment
-
-class TestAuthorizationAdjustment(unittest.TestCase):
-    def test_constructor(self):
-        attributes = {
-            "amount": "-20.00",
-            "timestamp": datetime(2017, 7, 12, 1, 2, 3),
-            "success": True,
-            "processor_response_code": "1000",
-            "processor_response_text": "Approved",
-        }
-
-        authorization_adjustment = AuthorizationAdjustment(attributes)
-
-        self.assertEqual(authorization_adjustment.amount, Decimal("-20.00"))
-        self.assertEqual(authorization_adjustment.timestamp, datetime(2017, 7, 12, 1, 2, 3))
-        self.assertEqual(authorization_adjustment.success, True)
-        self.assertEqual(authorization_adjustment.processor_response_code, "1000")
-        self.assertEqual(authorization_adjustment.processor_response_text, "Approved")
-
-    def test_constructor_with_amount_as_None(self):
-        attributes = {
-            "amount": None,
-            "timestamp": datetime(2017, 7, 12, 1, 2, 3),
-            "success": True,
-            "processor_response_code": "1000",
-        }
-
-        authorization_adjustment = AuthorizationAdjustment(attributes)
-
-        self.assertEqual(authorization_adjustment.amount, None)
-        self.assertEqual(authorization_adjustment.timestamp, datetime(2017, 7, 12, 1, 2, 3))
-        self.assertEqual(authorization_adjustment.success, True)
-        self.assertEqual(authorization_adjustment.processor_response_code, "1000")
-
-    def test_constructor_without_amount(self):
-        attributes = {
-            "timestamp": datetime(2017, 7, 12, 1, 2, 3),
-            "success": True,
-            "processor_response_code": "1000",
-            "processor_response_text": "Approved",
-        }
-
-        authorization_adjustment = AuthorizationAdjustment(attributes)
-
-        self.assertEqual(authorization_adjustment.timestamp, datetime(2017, 7, 12, 1, 2, 3))
-        self.assertEqual(authorization_adjustment.success, True)
-        self.assertEqual(authorization_adjustment.processor_response_code, "1000")
-        self.assertEqual(authorization_adjustment.processor_response_text, "Approved")
diff --git a/tests/unit/test_client_token.py b/tests/unit/test_client_token.py
deleted file mode 100644
index 7216a6b..0000000
--- a/tests/unit/test_client_token.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from tests.test_helper import *
-
-class TestClientToken(unittest.TestCase):
-    def test_credit_card_options_require_customer_id(self):
-        for option in ["verify_card", "make_default", "fail_on_duplicate_payment_method"]:
-            with self.assertRaisesRegexp(InvalidSignatureError, option):
-                ClientToken.generate({
-                    "options": {option: True}
-                })
-
-    def test_generate_delegates_client_token_generation_to_gateway(self):
-        class MockGateway:
-            def generate(self, _):
-                return "mock_client_token"
-
-        mock_gateway = MockGateway()
-        client_token = ClientToken.generate({}, mock_gateway)
-
-        self.assertEqual("mock_client_token", client_token)
diff --git a/tests/unit/test_configuration.py b/tests/unit/test_configuration.py
deleted file mode 100644
index cf82831..0000000
--- a/tests/unit/test_configuration.py
+++ /dev/null
@@ -1,169 +0,0 @@
-from tests.test_helper import *
-import braintree
-import os
-import imp
-
-class TestConfiguration(unittest.TestCase):
-    def test_works_with_unconfigured_configuration(self):
-        try:
-            # reset class level attributes on Configuration set in test helper
-            imp.reload(braintree.configuration)
-            config = Configuration(
-                environment=braintree.Environment.Sandbox,
-                merchant_id='my_merchant_id',
-                public_key='public_key',
-                private_key='private_key'
-            )
-            config.http_strategy()
-        except AttributeError as e:
-            print(e)
-            self.fail()
-        finally:
-            # repopulate class level attributes on Configuration
-            import tests.test_helper
-            imp.reload(tests.test_helper)
-
-    def test_base_merchant_path_for_development(self):
-        self.assertEqual("/merchants/integration_merchant_id", Configuration.instantiate().base_merchant_path())
-
-    def test_configuration_construction_for_merchant(self):
-        config = Configuration(
-            environment=braintree.Environment.Sandbox,
-            merchant_id='my_merchant_id',
-            public_key='public_key',
-            private_key='private_key'
-        )
-        self.assertEqual(config.merchant_id, 'my_merchant_id')
-        self.assertEqual(config.public_key, 'public_key')
-        self.assertEqual(config.private_key, 'private_key')
-
-    def test_configuration_configure_allows_strings_for_environment(self):
-        try:
-            for environment_string, environment_object in braintree.Environment.All.items():
-                braintree.Configuration.configure(
-                    environment_string,
-                    'my_merchant_id',
-                    'public_key',
-                    'private_key'
-                )
-                self.assertEqual(braintree.Configuration.environment, environment_object)
-        finally:
-            reset_braintree_configuration()
-
-    def test_configuration_construction_allows_strings_for_environment(self):
-        config = Configuration(
-            environment='sandbox',
-            merchant_id='my_merchant_id',
-            public_key='public_key',
-            private_key='private_key'
-        )
-
-        self.assertEqual(config.environment, braintree.Environment.Sandbox)
-
-    def test_configuration_construction_allows_empty_parameter_list(self):
-        config = Configuration()
-
-        self.assertIsInstance(config, braintree.Configuration)
-
-    def test_configuration_raises_configuration_error_for_invalid_environment(self):
-        for environment in [42, 'not_an_env', '']:
-            def setup_bad_configuration():
-                Configuration(
-                    environment=environment,
-                    merchant_id='my_merchant_id',
-                    public_key='public_key',
-                    private_key='private_key'
-                )
-
-            self.assertRaises(ConfigurationError, setup_bad_configuration)
-
-    def test_configuration_raises_configuration_error_for_empty_merchant_id(self):
-        def setup_bad_configuration():
-            Configuration(
-                environment=braintree.Environment.Sandbox,
-                merchant_id='',
-                public_key='public_key',
-                private_key='private_key'
-            )
-
-        self.assertRaises(ConfigurationError, setup_bad_configuration)
-
-    def test_configuration_raises_configuration_error_for_empty_public_key(self):
-        def setup_bad_configuration():
-            Configuration(
-                environment=braintree.Environment.Sandbox,
-                merchant_id='my_merchant_id',
-                public_key='',
-                private_key='private_key'
-            )
-
-        self.assertRaises(ConfigurationError, setup_bad_configuration)
-
-    def test_configuration_raises_configuration_error_for_empty_private_key(self):
-        def setup_bad_configuration():
-            Configuration(
-                environment=braintree.Environment.Sandbox,
-                merchant_id='my_merchant_id',
-                public_key='public_key',
-                private_key=''
-            )
-
-        self.assertRaises(ConfigurationError, setup_bad_configuration)
-
-    def test_configuration_construction_for_partner(self):
-        config = Configuration.for_partner(
-            braintree.Environment.Sandbox,
-            'my_partner_id',
-            'public_key',
-            'private_key'
-        )
-        self.assertEqual(config.merchant_id, 'my_partner_id')
-        self.assertEqual(config.public_key, 'public_key')
-        self.assertEqual(config.private_key, 'private_key')
-
-    def test_configuring_with_an_http_strategy(self):
-        class FakeStrategy(object):
-            def __init__(self, config, environment):
-                pass
-
-        strategy = Configuration(http_strategy=FakeStrategy).http_strategy()
-        self.assertIsInstance(strategy, FakeStrategy)
-
-    def test_partner_configuration_does_not_use_default_http_strategy(self):
-        old_http_strategy = Configuration.default_http_strategy
-
-        class FakeStrategy(object):
-            def __init__(self, config, environment):
-                pass
-
-        try:
-            Configuration.default_http_strategy = FakeStrategy
-            config = Configuration.for_partner(
-                braintree.Environment.Sandbox,
-                'my_partner_id',
-                'public_key',
-                'private_key'
-            )
-            self.assertNotIsInstance(config.http_strategy(), FakeStrategy)
-        finally:
-            Configuration.default_http_strategy = old_http_strategy
-
-    def test_instantiate_with_a_default_http_strategy(self):
-        old_http_strategy = Configuration.default_http_strategy
-
-        class FakeStrategy(object):
-            def __init__(self, config, environment):
-                pass
-
-        try:
-            Configuration.default_http_strategy = FakeStrategy
-            strategy = Configuration.instantiate().http_strategy()
-            self.assertIsInstance(strategy, FakeStrategy)
-        finally:
-            Configuration.default_http_strategy = old_http_strategy
-
-    def test_configuring_with_partial_client_credentials(self):
-        with self.assertRaises(ConfigurationError) as error:
-            Configuration(client_id='client_id$development$integration_client_id')
-
-        self.assertIn("Missing client_secret when constructing BraintreeGateway", str(error.exception))
diff --git a/tests/unit/test_credit_card.py b/tests/unit/test_credit_card.py
deleted file mode 100644
index a6ff4f5..0000000
--- a/tests/unit/test_credit_card.py
+++ /dev/null
@@ -1,94 +0,0 @@
-from tests.test_helper import *
-import datetime
-
-class TestCreditCard(unittest.TestCase):
-    @raises_with_regexp(KeyError, "'Invalid keys: bad_key'")
-    def test_create_raises_exception_with_bad_keys(self):
-        CreditCard.create({"bad_key": "value"})
-
-    @raises_with_regexp(KeyError, "'Invalid keys: bad_key'")
-    def test_update_raises_exception_with_bad_keys(self):
-        CreditCard.update("token", {"bad_key": "value"})
-
-    @raises_with_regexp(KeyError, "'Invalid keys: bad_key'")
-    def test_tr_data_for_create_raises_error_with_bad_keys(self):
-        CreditCard.tr_data_for_create({"bad_key": "value"}, "http://example.com")
-
-    @raises_with_regexp(KeyError, "'Invalid keys: bad_key'")
-    def test_tr_data_for_update_raises_error_with_bad_keys(self):
-        CreditCard.tr_data_for_update({"bad_key": "value"}, "http://example.com")
-
-    def test_transparent_redirect_create_url(self):
-        port = os.getenv("GATEWAY_PORT") or "3000"
-        self.assertEqual(
-            "http://localhost:" + port + "/merchants/integration_merchant_id/payment_methods/all/create_via_transparent_redirect_request",
-            CreditCard.transparent_redirect_create_url()
-        )
-
-    def test_transparent_redirect_update_url(self):
-        port = os.getenv("GATEWAY_PORT") or "3000"
-        self.assertEqual(
-            "http://localhost:" + port + "/merchants/integration_merchant_id/payment_methods/all/update_via_transparent_redirect_request",
-            CreditCard.transparent_redirect_update_url()
-        )
-
-    @raises(DownForMaintenanceError)
-    def test_confirm_transaprant_redirect_raises_error_given_503_status_in_query_string(self):
-        CreditCard.confirm_transparent_redirect(
-            "http_status=503&id=6kdj469tw7yck32j&hash=1b3d29199a282e63074a7823b76bccacdf732da6"
-        )
-
-    def test_create_signature(self):
-        expected = ["billing_address_id", "cardholder_name", "cvv", "expiration_date", "expiration_month",
-            "expiration_year", "device_session_id", "fraud_merchant_id", "number", "token", "venmo_sdk_payment_method_code",
-            "device_data", "payment_method_nonce",
-            {
-                "billing_address": [
-                    "company", "country_code_alpha2", "country_code_alpha3", "country_code_numeric", "country_name",
-                    "extended_address", "first_name", "last_name", "locality", "postal_code", "region", "street_address"
-                ]
-            },
-            {"options": ["make_default", "verification_merchant_account_id", "verify_card", "verification_amount", "verification_account_type", "venmo_sdk_session", "fail_on_duplicate_payment_method", {"adyen":["overwrite_brand", "selected_brand"]}
-            ]},
-            "customer_id"
-        ]
-        self.assertEqual(expected, CreditCard.create_signature())
-
-    def test_update_signature(self):
-        expected = ["billing_address_id", "cardholder_name", "cvv", "expiration_date", "expiration_month",
-            "expiration_year", "device_session_id", "fraud_merchant_id", "number", "token", "venmo_sdk_payment_method_code",
-            "device_data", "payment_method_nonce",
-            {
-                "billing_address": [
-                    "company", "country_code_alpha2", "country_code_alpha3", "country_code_numeric", "country_name",
-                    "extended_address", "first_name", "last_name", "locality", "postal_code", "region", "street_address",
-                    {"options": ["update_existing"]}
-                ]
-            },
-            {"options": ["make_default", "verification_merchant_account_id", "verify_card", "verification_amount", "verification_account_type", "venmo_sdk_session", "fail_on_duplicate_payment_method", {"adyen":["overwrite_brand", "selected_brand"]}
-            ]}
-        ]
-        self.assertEqual(expected, CreditCard.update_signature())
-
-    @raises(NotFoundError)
-    def test_finding_empty_id_raises_not_found_exception(self):
-        CreditCard.find(" ")
-
-    @raises(NotFoundError)
-    def test_finding_none_raises_not_found_exception(self):
-        CreditCard.find(None)
-
-    @raises(NotFoundError)
-    def test_from_nonce_empty_id_raises_not_found_exception(self):
-        CreditCard.from_nonce(" ")
-
-    @raises(NotFoundError)
-    def test_from_nonce_none_raises_not_found_exception(self):
-        CreditCard.from_nonce(None)
-
-    def test_multiple_verifications_sort(self):
-        verification1 = {"created_at": datetime.datetime(2014, 11, 18, 23, 20, 20), "id": 123, "amount": "0.00"}
-        verification2 = {"created_at": datetime.datetime(2014, 11, 18, 23, 20, 21), "id": 456, "amount": "1.00"}
-        credit_card = CreditCard(Configuration.gateway(), {"verifications": [verification1, verification2]})
-        self.assertEqual(456, credit_card.verification.id)
-        self.assertEqual(1.00, credit_card.verification.amount)
diff --git a/tests/unit/test_credit_card_verification.py b/tests/unit/test_credit_card_verification.py
deleted file mode 100644
index c0f90ec..0000000
--- a/tests/unit/test_credit_card_verification.py
+++ /dev/null
@@ -1,58 +0,0 @@
-from tests.test_helper import *
-
-class TestCreditCardVerification(unittest.TestCase):
-
-    @raises_with_regexp(KeyError, "'Invalid keys: bad_key'")
-    def test_create_raises_exception_with_bad_keys(self):
-        CreditCardVerification.create({"bad_key": "value", "credit_card": {"number": "value"}})
-
-    def test_constructor_with_amount(self):
-        attributes = {
-            'amount': '27.00',
-            'currency_iso_code': 'USD'
-        }
-        verification = CreditCardVerification(None, attributes)
-
-        self.assertEqual(verification.amount, Decimal('27.00'))
-        self.assertEqual(verification.currency_iso_code, 'USD')
-
-    def test_constructor_with_bad_amount(self):
-        attributes = {
-            'amount': None
-        }
-        verification = CreditCardVerification(None, attributes)
-
-        self.assertEqual(verification.amount, None)
-
-    def test_constructor_without_amount(self):
-        verification = CreditCardVerification(None, {})
-
-        self.assertEqual(verification.amount, None)
-        self.assertEqual(verification.currency_iso_code, None)
-
-    def test_constructor_when_risk_data_is_not_included(self):
-        verification = CreditCardVerification(None, {"amount": "1.00"})
-        self.assertEqual(verification.risk_data, None)
-
-    def test_constructor_when_network_response_is_included(self):
-        attributes = {
-            'amount': '1.00',
-            'network_response_code': '00',
-            'network_response_text': 'Successful approval/completion or V.I.P. PIN verification is successful'
-        }
-        verification = CreditCardVerification(None, attributes)
-        self.assertEqual(verification.network_response_code, '00')
-        self.assertEqual(verification.network_response_text, 'Successful approval/completion or V.I.P. PIN verification is successful')
-
-    def test_constructor_when_network_response_is_not_included(self):
-        verification = CreditCardVerification(None, {'amount': '1.00'})
-        self.assertEqual(verification.network_response_code, None)
-        self.assertEqual(verification.network_response_text, None)
-
-    @raises(NotFoundError)
-    def test_finding_empty_id_raises_not_found_exception(self):
-        CreditCardVerification.find(" ")
-
-    @raises(NotFoundError)
-    def test_finding_none_raises_not_found_exception(self):
-        CreditCardVerification.find(None)
diff --git a/tests/unit/test_crypto.py b/tests/unit/test_crypto.py
deleted file mode 100644
index 4c8074b..0000000
--- a/tests/unit/test_crypto.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from tests.test_helper import *
-
-class TestCrypto(unittest.TestCase):
-    def test_sha1_hmac_hash(self):
-        actual = Crypto.sha1_hmac_hash("secretKey", "hello world")
-        self.assertEqual("d503d7a1a6adba1e6474e9ff2c4167f9dfdf4247", actual)
-
-    def test_sha256_hmac_hash(self):
-        actual = Crypto.sha256_hmac_hash("secret-key", "secret-message")
-        self.assertEqual("68e7f2ecab71db67b1aca2a638f5122810315c3013f27c2196cd53e88709eecc", actual)
-
-    def test_secure_compare_returns_true_when_same(self):
-        self.assertTrue(Crypto.secure_compare("a_string", "a_string"))
-
-    def test_secure_compare_returns_false_when_different_lengths(self):
-        self.assertFalse(Crypto.secure_compare("a_string", "a_string_that_is_longer"))
-
-    def test_secure_compare_returns_false_when_different(self):
-        self.assertFalse(Crypto.secure_compare("a_string", "a_strong"))
diff --git a/tests/unit/test_customer.py b/tests/unit/test_customer.py
deleted file mode 100644
index b4f2d5c..0000000
--- a/tests/unit/test_customer.py
+++ /dev/null
@@ -1,46 +0,0 @@
-from tests.test_helper import *
-
-class TestCustomer(unittest.TestCase):
-    @raises_with_regexp(KeyError, "'Invalid keys: bad_key'")
-    def test_create_raise_exception_with_bad_keys(self):
-        Customer.create({"bad_key": "value"})
-
-    @raises_with_regexp(KeyError, "'Invalid keys: credit_card\[bad_key\]'")
-    def test_create_raise_exception_with_bad_nested_keys(self):
-        Customer.create({"credit_card": {"bad_key": "value"}})
-
-    @raises_with_regexp(KeyError, "'Invalid keys: bad_key'")
-    def test_update_raise_exception_with_bad_keys(self):
-        Customer.update("id", {"bad_key": "value"})
-
-    @raises_with_regexp(KeyError, "'Invalid keys: credit_card\[bad_key\]'")
-    def test_update_raise_exception_with_bad_nested_keys(self):
-        Customer.update("id", {"credit_card": {"bad_key": "value"}})
-
-    @raises_with_regexp(KeyError, "'Invalid keys: bad_key'")
-    def test_tr_data_for_create_raises_error_with_bad_keys(self):
-        Customer.tr_data_for_create({"bad_key": "value"}, "http://example.com")
-
-    @raises_with_regexp(KeyError, "'Invalid keys: bad_key'")
-    def test_tr_data_for_update_raises_error_with_bad_keys(self):
-        Customer.tr_data_for_update({"bad_key": "value"}, "http://example.com")
-
-    @raises(NotFoundError)
-    def test_finding_empty_id_raises_not_found_exception(self):
-        Customer.find(" ")
-
-    @raises(NotFoundError)
-    def test_finding_none_raises_not_found_exception(self):
-        Customer.find(None)
-
-    def test_initialize_sets_paypal_accounts(self):
-        customer = Customer("gateway", {
-            "paypal_accounts": [
-                {"token": "token1"},
-                {"token": "token2"}
-            ]
-        })
-
-        self.assertEqual(2, len(customer.paypal_accounts))
-        self.assertEqual("token1", customer.paypal_accounts[0].token)
-        self.assertEqual("token2", customer.paypal_accounts[1].token)
diff --git a/tests/unit/test_disbursement.py b/tests/unit/test_disbursement.py
deleted file mode 100644
index 3ce7914..0000000
--- a/tests/unit/test_disbursement.py
+++ /dev/null
@@ -1,42 +0,0 @@
-from tests.test_helper import *
-from datetime import date
-
-class TestDisbursement(unittest.TestCase):
-    attributes = {
-        "merchant_account": {
-            "id": "sub_merchant_account",
-            "status": "active",
-            "master_merchant_account": {
-                "id": "master_merchant_account",
-                "status": "active"
-            },
-        },
-        "id": "123456",
-        "exception_message": "invalid_account_number",
-        "amount": "100.00",
-        "disbursement_date": date(2013, 4, 10),
-        "follow_up_action": "update",
-        "transaction_ids": ["asdf", "qwer"],
-        "disbursement_type": "credit"
-    }
-
-    def test_constructor(self):
-        disbursement = Disbursement(None, TestDisbursement.attributes)
-
-        self.assertEqual("123456", disbursement.id)
-        self.assertEqual(Decimal("100.00"), disbursement.amount)
-        self.assertEqual(["asdf", "qwer"], disbursement.transaction_ids)
-        self.assertEqual("master_merchant_account", disbursement.merchant_account.master_merchant_account.id)
-
-    def test_credit(self):
-        disbursement = Disbursement(None, TestDisbursement.attributes)
-
-        self.assertTrue(disbursement.is_credit())
-
-    def test_debit(self):
-        thing = TestDisbursement.attributes
-        thing["disbursement_type"] = "debit"
-
-        disbursement = Disbursement(None, TestDisbursement.attributes)
-
-        self.assertTrue(disbursement.is_debit())
diff --git a/tests/unit/test_disbursement_detail.py b/tests/unit/test_disbursement_detail.py
deleted file mode 100644
index d1bcc6b..0000000
--- a/tests/unit/test_disbursement_detail.py
+++ /dev/null
@@ -1,27 +0,0 @@
-from tests.test_helper import *
-from braintree.disbursement_detail import DisbursementDetail
-
-class TestDisbursementDetail(unittest.TestCase):
-    def test_is_valid_true(self):
-        detail_hash = {
-            'settlement_amount': '27.00',
-            'settlement_currency_iso_code': 'USD',
-            'settlement_currency_exchange_rate': '1',
-            'disbursed_at': datetime(2013, 4, 11, 0, 0, 0),
-            'disbursement_date': date(2013, 4, 10),
-            'funds_held': False
-        }
-        disbursement_details = DisbursementDetail(detail_hash)
-        self.assertTrue(disbursement_details.is_valid)
-
-    def test_is_valid_false(self):
-        detail_hash = {
-            'settlement_amount': None,
-            'settlement_currency_iso_code': None,
-            'settlement_currency_exchange_rate': None,
-            'disbursed_at': None,
-            'disbursement_date': None,
-            'funds_held': None
-        }
-        disbursement_details = DisbursementDetail(detail_hash)
-        self.assertEqual(False, disbursement_details.is_valid)
diff --git a/tests/unit/test_dispute.py b/tests/unit/test_dispute.py
deleted file mode 100644
index 2332127..0000000
--- a/tests/unit/test_dispute.py
+++ /dev/null
@@ -1,251 +0,0 @@
-from tests.test_helper import *
-from datetime import date
-from braintree.dispute import Dispute
-
-class TestDispute(unittest.TestCase):
-    legacy_attributes = {
-        "transaction": {
-            "id": "transaction_id",
-            "amount": "100.00",
-        },
-        "id": "123456",
-        "currency_iso_code": "USD",
-        "status": "open",
-        "amount": "100.00",
-        "received_date": date(2013, 4, 10),
-        "reply_by_date": date(2013, 4, 10),
-        "reason": "fraud",
-        "transaction_ids": ["asdf", "qwer"],
-        "date_opened": date(2013, 4, 1),
-        "date_won": date(2013, 4, 2),
-        "kind": "chargeback",
-    }
-
-    attributes = {
-        "amount": "100.00",
-        "amount_disputed": "100.00",
-        "amount_won": "0.00",
-        "case_number": "CB123456",
-        "created_at": datetime(2013, 4, 10, 10, 50, 39),
-        "currency_iso_code": "USD",
-        "date_opened": date(2013, 4, 1),
-        "date_won": date(2013, 4, 2),
-        "processor_comments": "Forwarded comments",
-        "id": "123456",
-        "kind": "chargeback",
-        "merchant_account_id": "abc123",
-        "original_dispute_id": "original_dispute_id",
-        "reason": "fraud",
-        "reason_code": "83",
-        "reason_description": "Reason code 83 description",
-        "received_date": date(2013, 4, 10),
-        "reference_number": "123456",
-        "reply_by_date": date(2013, 4, 17),
-        "status": "open",
-        "updated_at": datetime(2013, 4, 10, 10, 50, 39),
-        "evidence": [{
-            "comment": None,
-            "created_at": datetime(2013, 4, 11, 10, 50, 39),
-            "id": "evidence1",
-            "sent_to_processor_at": None,
-            "url": "url_of_file_evidence",
-        },{
-            "comment": "text evidence",
-            "created_at": datetime(2013, 4, 11, 10, 50, 39),
-            "id": "evidence2",
-            "sent_to_processor_at": "2009-04-11",
-            "url": None,
-        }],
-        "status_history": [{
-            "disbursement_date": "2013-04-11",
-            "effective_date": "2013-04-10",
-            "status": "open",
-            "timestamp": datetime(2013, 4, 10, 10, 50, 39),
-        }],
-        "transaction": {
-            "id": "transaction_id",
-            "amount": "100.00",
-            "created_at": datetime(2013, 3, 19, 10, 50, 39),
-            "order_id": None,
-            "purchase_order_number": "po",
-            "payment_instrument_subtype": "Visa",
-        },
-    }
-
-    def test_legacy_constructor(self):
-        dispute = Dispute(dict(self.legacy_attributes))
-
-        self.assertEqual(dispute.id, "123456")
-        self.assertEqual(dispute.amount, Decimal("100.00"))
-        self.assertEqual(dispute.currency_iso_code, "USD")
-        self.assertEqual(dispute.reason, Dispute.Reason.Fraud)
-        self.assertEqual(dispute.status, Dispute.Status.Open)
-        self.assertEqual(dispute.transaction_details.id, "transaction_id")
-        self.assertEqual(dispute.transaction_details.amount, Decimal("100.00"))
-        self.assertEqual(dispute.date_opened, date(2013, 4, 1))
-        self.assertEqual(dispute.date_won, date(2013, 4, 2))
-        self.assertEqual(dispute.kind, Dispute.Kind.Chargeback)
-
-    def test_legacy_params_with_new_attributes(self):
-        dispute = Dispute(dict(self.attributes))
-
-        self.assertEqual(dispute.id, "123456")
-        self.assertEqual(dispute.amount, Decimal("100.00"))
-        self.assertEqual(dispute.currency_iso_code, "USD")
-        self.assertEqual(dispute.reason, Dispute.Reason.Fraud)
-        self.assertEqual(dispute.status, Dispute.Status.Open)
-        self.assertEqual(dispute.transaction_details.id, "transaction_id")
-        self.assertEqual(dispute.transaction_details.amount, Decimal("100.00"))
-        self.assertEqual(dispute.date_opened, date(2013, 4, 1))
-        self.assertEqual(dispute.date_won, date(2013, 4, 2))
-        self.assertEqual(dispute.kind, Dispute.Kind.Chargeback)
-
-    def test_constructor_populates_new_fields(self):
-        attributes = dict(self.attributes)
-        del attributes["amount"]
-
-        dispute = Dispute(attributes)
-
-        self.assertEqual(dispute.amount_disputed, 100.0)
-        self.assertEqual(dispute.amount_won, 0.00)
-        self.assertEqual(dispute.case_number, "CB123456")
-        self.assertEqual(dispute.created_at, datetime(2013, 4, 10, 10, 50, 39))
-        self.assertEqual(dispute.forwarded_comments, "Forwarded comments")
-        self.assertEqual(dispute.processor_comments, "Forwarded comments")
-        self.assertEqual(dispute.merchant_account_id, "abc123")
-        self.assertEqual(dispute.original_dispute_id, "original_dispute_id")
-        self.assertEqual(dispute.reason_code, "83")
-        self.assertEqual(dispute.reason_description, "Reason code 83 description")
-        self.assertEqual(dispute.reference_number, "123456")
-        self.assertEqual(dispute.updated_at, datetime(2013, 4, 10, 10, 50, 39))
-        self.assertIsNone(dispute.evidence[0].comment)
-        self.assertEqual(dispute.evidence[0].created_at, datetime(2013, 4, 11, 10, 50, 39))
-        self.assertEqual(dispute.evidence[0].id, "evidence1")
-        self.assertIsNone(dispute.evidence[0].sent_to_processor_at)
-        self.assertEqual(dispute.evidence[0].url, "url_of_file_evidence")
-        self.assertEqual(dispute.evidence[1].comment, "text evidence")
-        self.assertEqual(dispute.evidence[1].created_at, datetime(2013, 4, 11, 10, 50, 39))
-        self.assertEqual(dispute.evidence[1].id, "evidence2")
-        self.assertEqual(dispute.evidence[1].sent_to_processor_at, "2009-04-11")
-        self.assertIsNone(dispute.evidence[1].url)
-        self.assertEqual(dispute.status_history[0].disbursement_date, "2013-04-11")
-        self.assertEqual(dispute.status_history[0].effective_date, "2013-04-10")
-        self.assertEqual(dispute.status_history[0].status, "open")
-        self.assertEqual(dispute.status_history[0].timestamp, datetime(2013, 4, 10, 10, 50, 39))
-
-    def test_constructor_handles_none_fields(self):
-        attributes = dict(self.attributes)
-        attributes.update({
-            "amount": None,
-            "date_opened": None,
-            "date_won": None,
-            "evidence": None,
-            "reply_by_date": None,
-            "status_history": None
-        })
-
-        dispute = Dispute(attributes)
-
-        self.assertIsNone(dispute.reply_by_date)
-        self.assertIsNone(dispute.amount)
-        self.assertIsNone(dispute.date_opened)
-        self.assertIsNone(dispute.date_won)
-        self.assertIsNone(dispute.status_history)
-
-    def test_constructor_populates_transaction(self):
-        dispute = Dispute(dict(self.attributes))
-
-        self.assertEqual(dispute.transaction.id, "transaction_id")
-        self.assertEqual(dispute.transaction.amount, Decimal("100.00"))
-        self.assertEqual(dispute.transaction.created_at, datetime(2013, 3, 19, 10, 50, 39))
-        self.assertIsNone(dispute.transaction.order_id)
-        self.assertEqual(dispute.transaction.purchase_order_number, "po")
-        self.assertEqual(dispute.transaction.payment_instrument_subtype, "Visa")
-
-    @raises_with_regexp(NotFoundError, "dispute with id None not found")
-    def test_accept_none_raises_not_found_exception(self):
-        Dispute.accept(None)
-
-    @raises_with_regexp(NotFoundError, "dispute with id ' ' not found")
-    def test_accept_empty_id_raises_not_found_exception(self):
-        Dispute.accept(" ")
-
-    @raises_with_regexp(NotFoundError, "dispute_id cannot be blank")
-    def test_add_text_evidence_empty_id_raises_not_found_exception(self):
-        Dispute.add_text_evidence(" ", "evidence")
-
-    @raises_with_regexp(NotFoundError, "dispute_id cannot be blank")
-    def test_add_text_evidence_none_id_raises_not_found_exception(self):
-        Dispute.add_text_evidence(None, "evidence")
-
-    @raises_with_regexp(ValueError, "content cannot be blank")
-    def test_add_text_evidence_empty_evidence_raises_value_exception(self):
-        Dispute.add_text_evidence("dispute_id", " ")
-
-    @raises_with_regexp(ValueError, "sequence_number must be an integer")
-    def test_add_text_evidence_sequence_number_not_number_evidence_raises_value_exception(self):
-        Dispute.add_text_evidence("dispute_id", { "content": "content", "sequence_number": "a" })
-
-    @raises_with_regexp(ValueError, "sequence_number must be an integer")
-    def test_add_text_evidence_sequence_number_not_number_evidence_raises_value_exception(self):
-        Dispute.add_text_evidence("dispute_id", { "content": "content", "sequence_number": "1abc" })
-
-    @raises_with_regexp(ValueError, "category must be a string")
-    def test_add_text_evidence_category_is_number_evidence_raises_value_exception(self):
-        Dispute.add_text_evidence("dispute_id", { "content": "content", "category": 5 })
-
-    @raises_with_regexp(NotFoundError, "dispute with id ' ' not found")
-    def test_add_file_evidence_empty_id_raises_not_found_exception(self):
-        Dispute.add_file_evidence(" ", 1)
-
-    @raises_with_regexp(NotFoundError, "dispute with id None not found")
-    def test_add_file_evidence_none_id_raises_not_found_exception(self):
-        Dispute.add_file_evidence(None, 1)
-
-    @raises_with_regexp(ValueError, "document_id cannot be blank")
-    def test_add_file_evidence_empty_evidence_raises_value_exception(self):
-        Dispute.add_file_evidence("dispute_id", " ")
-
-    @raises_with_regexp(ValueError, "document_id cannot be blank")
-    def test_add_file_evidence_none_evidence_raises_value_exception(self):
-        Dispute.add_file_evidence("dispute_id", None)
-
-    @raises_with_regexp(ValueError, "category must be a string")
-    def test_add_file_evidence_categorized_document_id_must_be_a_string(self):
-        Dispute.add_file_evidence("dispute_id", { "document_id": "213", "category": 5 })
-
-    @raises_with_regexp(ValueError, "document_id cannot be blank")
-    def test_add_file_evidence_empty_categorized_evidence_raises_value_exception(self):
-        Dispute.add_file_evidence("dispute_id", { "category": "DEVICE_ID" })
-
-    @raises_with_regexp(NotFoundError, "dispute with id None not found")
-    def test_finalize_none_raises_not_found_exception(self):
-        Dispute.finalize(None)
-
-    @raises_with_regexp(NotFoundError, "dispute with id ' ' not found")
-    def test_finalize_empty_id_raises_not_found_exception(self):
-        Dispute.finalize(" ")
-
-    @raises_with_regexp(NotFoundError, "dispute with id None not found")
-    def test_finding_none_raises_not_found_exception(self):
-        Dispute.find(None)
-
-    @raises_with_regexp(NotFoundError, "dispute with id ' ' not found")
-    def test_finding_empty_id_raises_not_found_exception(self):
-        Dispute.find(" ")
-
-    @raises_with_regexp(NotFoundError, "evidence with id 'evidence' for dispute with id ' ' not found")
-    def test_remove_evidence_empty_dispute_id_raises_not_found_exception(self):
-        Dispute.remove_evidence(" ", "evidence")
-
-    @raises_with_regexp(NotFoundError, "evidence with id 'evidence' for dispute with id None not found")
-    def test_remove_evidence_none_dispute_id_raises_not_found_exception(self):
-        Dispute.remove_evidence(None, "evidence")
-
-    @raises_with_regexp(NotFoundError, "evidence with id None for dispute with id 'dispute_id' not found")
-    def test_remove_evidence_evidence_none_id_raises_not_found_exception(self):
-        Dispute.remove_evidence("dispute_id", None)
-
-    @raises_with_regexp(NotFoundError, "evidence with id ' ' for dispute with id 'dispute_id' not found")
-    def test_remove_evidence_empty_evidence_id_raises_value_exception(self):
-        Dispute.remove_evidence("dispute_id", " ")
diff --git a/tests/unit/test_document_upload.py b/tests/unit/test_document_upload.py
deleted file mode 100644
index 8ebecd8..0000000
--- a/tests/unit/test_document_upload.py
+++ /dev/null
@@ -1,6 +0,0 @@
-from tests.test_helper import *
-
-class TestDocumentUpload(unittest.TestCase):
-    @raises_with_regexp(KeyError, "'Invalid keys: bad_key'")
-    def test_create_raises_exception_with_bad_keys(self):
-        DocumentUpload.create({"bad_key": "value"})
diff --git a/tests/unit/test_environment.py b/tests/unit/test_environment.py
deleted file mode 100644
index c89eae5..0000000
--- a/tests/unit/test_environment.py
+++ /dev/null
@@ -1,57 +0,0 @@
-from tests.test_helper import *
-
-class TestEnvironment(unittest.TestCase):
-    def test_server_and_port_for_development(self):
-        port = os.getenv("GATEWAY_PORT") or "3000"
-        self.assertEqual("localhost:" + port, Environment.Development.server_and_port)
-
-    def test_base_url(self):
-        self.assertEqual("https://api.sandbox.braintreegateway.com:443", Environment.Sandbox.base_url)
-        self.assertEqual("https://api.braintreegateway.com:443", Environment.Production.base_url)
-
-    def test_server_and_port_for_sandbox(self):
-        self.assertEqual("api.sandbox.braintreegateway.com:443", Environment.Sandbox.server_and_port)
-
-    def test_server_and_port_for_production(self):
-        self.assertEqual("api.braintreegateway.com:443", Environment.Production.server_and_port)
-
-    def test_server_for_development(self):
-        self.assertEqual("localhost", Environment.Development.server)
-
-    def test_server_for_sandbox(self):
-        self.assertEqual("api.sandbox.braintreegateway.com", Environment.Sandbox.server)
-
-    def test_server_for_production(self):
-        self.assertEqual("api.braintreegateway.com", Environment.Production.server)
-
-    def test_port_for_development(self):
-        port = os.getenv("GATEWAY_PORT") or "3000"
-        port = int(port)
-        self.assertEqual(port, Environment.Development.port)
-
-    def test_port_for_sandbox(self):
-        self.assertEqual(443, Environment.Sandbox.port)
-
-    def test_port_for_production(self):
-        self.assertEqual(443, Environment.Production.port)
-
-    def test_is_ssl_for_development(self):
-        self.assertFalse(Environment.Development.is_ssl)
-
-    def test_is_ssl_for_sandbox(self):
-        self.assertTrue(Environment.Sandbox.is_ssl)
-
-    def test_is_ssl_for_production(self):
-        self.assertTrue(Environment.Production.is_ssl)
-
-    def test_protocol_for_development(self):
-        self.assertEqual("http://", Environment.Development.protocol)
-
-    def test_protocol_for_sandbox(self):
-        self.assertEqual("https://", Environment.Sandbox.protocol)
-
-    def test_protocol_for_production(self):
-        self.assertEqual("https://", Environment.Production.protocol)
-
-    def test_ssl_certificate_for_development(self):
-        self.assertEqual(None, Environment.Development.ssl_certificate)
diff --git a/tests/unit/test_error_result.py b/tests/unit/test_error_result.py
deleted file mode 100644
index 37aa097..0000000
--- a/tests/unit/test_error_result.py
+++ /dev/null
@@ -1,35 +0,0 @@
-from tests.test_helper import *
-
-class TestErrorResult(unittest.TestCase):
-    def test_it_initializes_params_and_errors(self):
-        errors = {
-            "scope": {
-                "errors": [{"code": 123, "message": "something is invalid", "attribute": "something"}]
-            }
-        }
-
-        result = ErrorResult("gateway", {"errors": errors, "params": "params", "message": "brief description"})
-        self.assertFalse(result.is_success)
-        self.assertEqual("params", result.params)
-        self.assertEqual(1, result.errors.size)
-        self.assertEqual("something is invalid", result.errors.for_object("scope")[0].message)
-        self.assertEqual("something", result.errors.for_object("scope")[0].attribute)
-        self.assertEqual(123, result.errors.for_object("scope")[0].code)
-
-    def test_it_ignores_other_params(self):
-        errors = {
-            "scope": {
-                "errors": [{"code": 123, "message": "something is invalid", "attribute": "something"}]
-            }
-        }
-
-        result = ErrorResult("gateway", {"errors": errors, "params": "params", "message": "brief description", "other": "stuff"})
-        self.assertFalse(result.is_success)
-
-    def test_transaction_is_none_if_not_set(self):
-        result = ErrorResult("gateway", {"errors": {}, "params": {}, "message": "brief description"})
-        self.assertTrue(result.transaction is None)
-
-    def test_verification_is_none_if_not_set(self):
-        result = ErrorResult("gateway", {"errors": {}, "params": {}, "message": "brief description"})
-        self.assertTrue(result.credit_card_verification is None)
diff --git a/tests/unit/test_errors.py b/tests/unit/test_errors.py
deleted file mode 100644
index f2a7d2b..0000000
--- a/tests/unit/test_errors.py
+++ /dev/null
@@ -1,50 +0,0 @@
-from tests.test_helper import *
-
-class TestErrors(unittest.TestCase):
-    def test_errors_for_the_given_scope(self):
-        errors = Errors({"level1": {"errors": [{"code": "code1", "attribute": "attr", "message": "message"}]}})
-        self.assertEqual(1, errors.for_object("level1").size)
-        self.assertEqual(1, len(errors.for_object("level1")))
-
-    def test_for_object_returns_empty_errors_collection_if_no_errors_at_given_scope(self):
-        errors = Errors({"level1": {"errors": [{"code": "code1", "attribute": "attr", "message": "message"}]}})
-        self.assertEqual(0, errors.for_object("no_errors_here").size)
-        self.assertEqual(0, len(errors.for_object("no_errors_here")))
-
-    def test_size_returns_number_of_errors_at_first_level_if_only_one_level_exists(self):
-        test_hash = {
-            "level1": {"errors": [{"code": "code1", "attribute": "attr", "message": "message"}]}
-        }
-        self.assertEqual(1, Errors(test_hash).size)
-        self.assertEqual(1, len(Errors(test_hash)))
-
-    def test_size_returns_number_of_errors_at_all_levels(self):
-        test_hash = {
-            "level1": {
-                "errors": [{"code": "code1", "attribute": "attr", "message": "message"}],
-                "level2": {
-                    "errors": [
-                        {"code": "code2", "attribute": "attr", "message": "message"},
-                        {"code": "code3", "attribute": "attr", "message": "message"}
-                    ]
-                }
-            }
-        }
-        self.assertEqual(3, Errors(test_hash).size)
-        self.assertEqual(3, len(Errors(test_hash)))
-
-    def test_deep_errors_returns_all_errors(self):
-        test_hash = {
-            "level1": {
-                "errors": [{"code": "code1", "attribute": "attr", "message": "message"}],
-                "level2": {
-                    "errors": [
-                        {"code": "code2", "attribute": "attr", "message": "message"},
-                        {"code": "code3", "attribute": "attr", "message": "message"}
-                    ]
-                }
-            }
-        }
-
-        errors = Errors(test_hash).deep_errors
-        self.assertEqual(["code1", "code2", "code3"], [error.code for error in errors])
diff --git a/tests/unit/test_europe_bank_account.py b/tests/unit/test_europe_bank_account.py
deleted file mode 100644
index eb684a4..0000000
--- a/tests/unit/test_europe_bank_account.py
+++ /dev/null
@@ -1,6 +0,0 @@
-from tests.test_helper import *
-
-class TestEuropeBankAccount(unittest.TestCase):
-    def test_mandate_type_constants(self):
-        self.assertEqual("business", EuropeBankAccount.MandateType.Business)
-        self.assertEqual("consumer", EuropeBankAccount.MandateType.Consumer)
diff --git a/tests/unit/test_exports.py b/tests/unit/test_exports.py
deleted file mode 100644
index 8f1ce47..0000000
--- a/tests/unit/test_exports.py
+++ /dev/null
@@ -1,75 +0,0 @@
-from tests.test_helper import *
-import braintree
-
-class TestExports(unittest.TestCase):
-    def test_exports_properties(self):
-        self.assertNotEqual(braintree.AchMandate, None)
-        self.assertNotEqual(braintree.AddOn, None)
-        self.assertNotEqual(braintree.AddOnGateway, None)
-        self.assertNotEqual(braintree.Address, None)
-        self.assertNotEqual(braintree.AddressGateway, None)
-        self.assertNotEqual(braintree.AmexExpressCheckoutCard, None)
-        self.assertNotEqual(braintree.AndroidPayCard, None)
-        self.assertNotEqual(braintree.ApplePayCard, None)
-        self.assertNotEqual(braintree.BraintreeGateway, None)
-        self.assertNotEqual(braintree.ClientToken, None)
-        self.assertNotEqual(braintree.Configuration, None)
-        self.assertNotEqual(braintree.CredentialsParser, None)
-        self.assertNotEqual(braintree.CreditCard, None)
-        self.assertNotEqual(braintree.CreditCardGateway, None)
-        self.assertNotEqual(braintree.CreditCardVerification, None)
-        self.assertNotEqual(braintree.CreditCardVerificationSearch, None)
-        self.assertNotEqual(braintree.Customer, None)
-        self.assertNotEqual(braintree.CustomerGateway, None)
-        self.assertNotEqual(braintree.CustomerSearch, None)
-        self.assertNotEqual(braintree.Descriptor, None)
-        self.assertNotEqual(braintree.Disbursement, None)
-        self.assertNotEqual(braintree.Discount, None)
-        self.assertNotEqual(braintree.DiscountGateway, None)
-        self.assertNotEqual(braintree.Environment, None)
-        self.assertNotEqual(braintree.ErrorCodes, None)
-        self.assertNotEqual(braintree.ErrorResult, None)
-        self.assertNotEqual(braintree.Errors, None)
-        self.assertNotEqual(braintree.EuropeBankAccount, None)
-        # NEXT_MAJOR_VERSION Remove this class as legacy Ideal has been removed/disabled in the Braintree Gateway
-        # DEPRECATED If you're looking to accept iDEAL as a payment method contact accounts@braintreepayments.com for a solution.
-        self.assertNotEqual(braintree.IdealPayment, None)
-        self.assertNotEqual(braintree.Merchant, None)
-        self.assertNotEqual(braintree.MerchantAccount, None)
-        self.assertNotEqual(braintree.MerchantAccountGateway, None)
-        self.assertNotEqual(braintree.PartnerMerchant, None)
-        self.assertNotEqual(braintree.PaymentInstrumentType, None)
-        self.assertNotEqual(braintree.PaymentMethod, None)
-        self.assertNotEqual(braintree.PaymentMethodNonce, None)
-        self.assertNotEqual(braintree.PayPalAccount, None)
-        self.assertNotEqual(braintree.Plan, None)
-        self.assertNotEqual(braintree.PlanGateway, None)
-        self.assertNotEqual(braintree.ResourceCollection, None)
-        self.assertNotEqual(braintree.RiskData, None)
-        self.assertNotEqual(braintree.Search, None)
-        self.assertNotEqual(braintree.SettlementBatchSummary, None)
-        self.assertNotEqual(braintree.SignatureService, None)
-        self.assertNotEqual(braintree.StatusEvent, None)
-        self.assertNotEqual(braintree.Subscription, None)
-        self.assertNotEqual(braintree.SubscriptionGateway, None)
-        self.assertNotEqual(braintree.SubscriptionSearch, None)
-        self.assertNotEqual(braintree.SubscriptionStatusEvent, None)
-        self.assertNotEqual(braintree.SuccessfulResult, None)
-        self.assertNotEqual(braintree.TestingGateway, None)
-        self.assertNotEqual(braintree.ThreeDSecureInfo, None)
-        self.assertNotEqual(braintree.Transaction, None)
-        self.assertNotEqual(braintree.TransactionAmounts, None)
-        self.assertNotEqual(braintree.TransactionDetails, None)
-        self.assertNotEqual(braintree.TransactionGateway, None)
-        self.assertNotEqual(braintree.TransactionSearch, None)
-        self.assertNotEqual(braintree.TransparentRedirect, None)
-        self.assertNotEqual(braintree.TransparentRedirectGateway, None)
-        self.assertNotEqual(braintree.UnknownPaymentMethod, None)
-        self.assertNotEqual(braintree.UsBankAccount, None)
-        self.assertNotEqual(braintree.ValidationErrorCollection, None)
-        self.assertNotEqual(braintree.VenmoAccount, None)
-        self.assertNotEqual(braintree.Version, None)
-        self.assertNotEqual(braintree.WebhookNotification, None)
-        self.assertNotEqual(braintree.WebhookNotificationGateway, None)
-        self.assertNotEqual(braintree.WebhookTesting, None)
-        self.assertNotEqual(braintree.WebhookTestingGateway, None)
diff --git a/tests/unit/test_graphql_client.py b/tests/unit/test_graphql_client.py
deleted file mode 100644
index 7df00d9..0000000
--- a/tests/unit/test_graphql_client.py
+++ /dev/null
@@ -1,63 +0,0 @@
-from tests.test_helper import *
-
-class TestGraphQLClient(unittest.TestCase):
-    @raises(UpgradeRequiredError)
-    def test_raise_exception_from_status_for_upgrade_required(self):
-        response = {
-            "errors": [
-                {
-                    "message": "error message",
-                    "extensions": {
-                        "errorClass": "UNSUPPORTED_CLIENT"
-                    }
-                }
-            ]
-        }
-        GraphQLClient.raise_exception_for_graphql_error(response)
-
-    @raises(TooManyRequestsError)
-    def test_raise_exception_from_too_many_requests(self):
-        response = {
-            "errors": [
-                {
-                    "message": "error message",
-                    "extensions": {
-                        "errorClass": "RESOURCE_LIMIT"
-                    }
-                }
-            ]
-        }
-        GraphQLClient.raise_exception_for_graphql_error(response)
-
-    def test_does_not_raise_exception_from_validation_error(self):
-        response = {
-            "errors": [
-                {
-                    "message": "error message",
-                    "extensions": {
-                        "errorClass": "VALIDATION"
-                    }
-                }
-            ]
-        }
-        GraphQLClient.raise_exception_for_graphql_error(response)
-
-    @raises(ServerError)
-    def test_raise_exception_from_validation_error_and_legitimate_error(self):
-        response = {
-            "errors": [
-                {
-                    "message": "error message",
-                    "extensions": {
-                        "errorClass": "VALIDATION"
-                    }
-                },
-                {
-                    "message": "error message 2",
-                    "extensions": {
-                        "errorClass": "INTERNAL"
-                    }
-                }
-            ]
-        }
-        GraphQLClient.raise_exception_for_graphql_error(response)
diff --git a/tests/unit/test_http.py b/tests/unit/test_http.py
deleted file mode 100644
index 661d9b0..0000000
--- a/tests/unit/test_http.py
+++ /dev/null
@@ -1,127 +0,0 @@
-import traceback
-
-from tests.test_helper import *
-from braintree.exceptions.http.timeout_error import *
-from braintree.attribute_getter import AttributeGetter
-
-class TestHttp(unittest.TestCase):
-    @raises(UpgradeRequiredError)
-    def test_raise_exception_from_status_for_upgrade_required(self):
-        Http.raise_exception_from_status(426)
-
-    @raises(TooManyRequestsError)
-    def test_raise_exception_from_too_many_requests(self):
-        Http.raise_exception_from_status(429)
-
-    def test_header_includes_gzip_accept_encoding(self):
-        config = AttributeGetter({
-                "base_url": (lambda: ""),
-                "has_access_token": (lambda: False),
-                "has_client_credentials": (lambda: False),
-                "public_key": "",
-                "private_key": ""})
-        headers = Http(config, "fake_environment")._Http__headers(Http.ContentType.Xml)
-        self.assertTrue('Accept-Encoding' in headers)
-        self.assertEqual('gzip', headers["Accept-Encoding"])
-
-    def test_backtrace_preserved_when_not_wrapping_exceptions(self):
-        class Error(Exception):
-            pass
-        def raise_error(*_):
-            raise Error
-        http_strategy = AttributeGetter({"http_do": raise_error})
-        config = AttributeGetter({
-                "base_url": (lambda: ""),
-                "has_access_token": (lambda: False),
-                "has_client_credentials": (lambda: False),
-                "http_strategy": (lambda: http_strategy),
-                "public_key": "",
-                "private_key": "",
-                "wrap_http_exceptions": False})
-
-        try:
-            Http(config, "fake_environment").post("/example/path/to/reach")
-        except Error:
-            _, _, tb = sys.exc_info()
-            self.assertEqual('raise_error', traceback.extract_tb(tb)[-1][2])
-
-    def test_request_body_returns_string_for_post(self):
-        def test_http_do_strategy(http_verb, path, headers, request_body):
-            self.assertTrue(isinstance(request_body, str))
-            self.assertEqual("<method>post</method>", request_body)
-            return (200, "")
-
-        http = self.setup_http_strategy(test_http_do_strategy)
-        http.post("/some_path", {"method": "post"})
-
-    def test_request_body_returns_string_for_delete(self):
-        def test_http_do_strategy(http_verb, path, headers, request_body):
-            self.assertTrue(isinstance(request_body, str))
-            return (200, "")
-
-        http = self.setup_http_strategy(test_http_do_strategy)
-        http.delete("/some_path")
-
-    def test_request_body_returns_string_for_get(self):
-        def test_http_do_strategy(http_verb, path, headers, request_body):
-            self.assertTrue(isinstance(request_body, str))
-            return (200, "")
-
-        http = self.setup_http_strategy(test_http_do_strategy)
-        http.get("/some_path")
-
-    def test_request_body_returns_string_for_put(self):
-        def test_http_do_strategy(http_verb, path, headers, request_body):
-            self.assertTrue(isinstance(request_body, str))
-            self.assertEqual("<method>put</method>", request_body)
-            return (200, "")
-
-        http = self.setup_http_strategy(test_http_do_strategy)
-        http.put("/some_path", {"method": "put"})
-
-    def test_request_body_returns_string_for_post_multipart_when_no_files(self):
-        params = {"method": "post_multipart"}
-        def test_http_do_strategy(http_verb, path, headers, request_body):
-            self.assertEqual(params, request_body)
-            return (200, "")
-
-        http = self.setup_http_strategy(test_http_do_strategy)
-        http.post_multipart("/some_path", None, params)
-
-    def test_request_body_returns_tuple_for_post_multipart_when_files(self):
-        params = {"method": "post_multipart"}
-        def test_http_do_strategy(http_verb, path, headers, request_body):
-            self.assertEqual(params, request_body[0])
-            self.assertEqual("files", request_body[1])
-            return (200, "")
-
-        http = self.setup_http_strategy(test_http_do_strategy)
-        http.post_multipart("/some_path", "files", params)
-
-    def setup_http_strategy(self, http_do):
-        config = AttributeGetter({
-                "base_url": (lambda: ""),
-                "has_access_token": (lambda: False),
-                "has_client_credentials": (lambda: False),
-                "http_strategy": (lambda: AttributeGetter({"http_do": http_do})),
-                "public_key": "",
-                "private_key": "",
-                "wrap_http_exceptions": False})
-
-        return Http(config, "fake_environment")
-
-    @raises(ReadTimeoutError)
-    def test_raise_read_timeout_error(self):
-        def test_http_do_strategy(http_verb, path, headers, request_body):
-            return (200, "")
-
-        http = self.setup_http_strategy(test_http_do_strategy)
-        http.handle_exception(requests.exceptions.ReadTimeout())
-
-    @raises(ConnectTimeoutError)
-    def test_raise_read_timeout_error(self):
-        def test_http_do_strategy(http_verb, path, headers, request_body):
-            return (200, "")
-
-        http = self.setup_http_strategy(test_http_do_strategy)
-        http.handle_exception(requests.exceptions.ConnectTimeout())
diff --git a/tests/unit/test_merchant_account.py b/tests/unit/test_merchant_account.py
deleted file mode 100644
index 38add47..0000000
--- a/tests/unit/test_merchant_account.py
+++ /dev/null
@@ -1,57 +0,0 @@
-from tests.test_helper import *
-
-class TestMerchantAccount(unittest.TestCase):
-    def test_create_new_merchant_account_with_all_params(self):
-        params = {
-            "id": "sub_merchant_account",
-            "status": "active",
-            "master_merchant_account": {
-                "id": "master_merchant_account",
-                "status": "active"
-            },
-            "individual": {
-                "first_name": "John",
-                "last_name": "Doe",
-                "email": "john.doe@example.com",
-                "date_of_birth": "1970-01-01",
-                "phone": "3125551234",
-                "ssn_last_4": "6789",
-                "address": {
-                    "street_address": "123 Fake St",
-                    "locality": "Chicago",
-                    "region": "IL",
-                    "postal_code": "60622",
-                }
-            },
-            "business": {
-                "dba_name": "James's Bloggs",
-                "tax_id": "123456789",
-            },
-            "funding": {
-                "account_number_last_4": "8798",
-                "routing_number": "071000013",
-                "descriptor": "Joes Bloggs MI",
-            }
-        }
-
-        merchant_account = MerchantAccount(None, params)
-
-        self.assertEqual(merchant_account.status, "active")
-        self.assertEqual(merchant_account.id, "sub_merchant_account")
-        self.assertEqual(merchant_account.master_merchant_account.id, "master_merchant_account")
-        self.assertEqual(merchant_account.master_merchant_account.status, "active")
-        self.assertEqual(merchant_account.individual_details.first_name, "John")
-        self.assertEqual(merchant_account.individual_details.last_name, "Doe")
-        self.assertEqual(merchant_account.individual_details.email, "john.doe@example.com")
-        self.assertEqual(merchant_account.individual_details.date_of_birth, "1970-01-01")
-        self.assertEqual(merchant_account.individual_details.phone, "3125551234")
-        self.assertEqual(merchant_account.individual_details.ssn_last_4, "6789")
-        self.assertEqual(merchant_account.individual_details.address_details.street_address, "123 Fake St")
-        self.assertEqual(merchant_account.individual_details.address_details.locality, "Chicago")
-        self.assertEqual(merchant_account.individual_details.address_details.region, "IL")
-        self.assertEqual(merchant_account.individual_details.address_details.postal_code, "60622")
-        self.assertEqual(merchant_account.business_details.dba_name, "James's Bloggs")
-        self.assertEqual(merchant_account.business_details.tax_id, "123456789")
-        self.assertEqual(merchant_account.funding_details.account_number_last_4, "8798")
-        self.assertEqual(merchant_account.funding_details.routing_number, "071000013")
-        self.assertEqual(merchant_account.funding_details.descriptor, "Joes Bloggs MI")
diff --git a/tests/unit/test_oauth_access_revocation.py b/tests/unit/test_oauth_access_revocation.py
deleted file mode 100644
index eace1ff..0000000
--- a/tests/unit/test_oauth_access_revocation.py
+++ /dev/null
@@ -1,8 +0,0 @@
-from tests.test_helper import *
-import datetime
-
-class TestOAuthAccessRevocation(unittest.TestCase):
-    def test_assigns_merchant_id(self):
-        revocation = OAuthAccessRevocation({"merchant_id": "abc123xyz"})
-
-        self.assertEqual(revocation.merchant_id, "abc123xyz")
diff --git a/tests/unit/test_paginated_collection.py b/tests/unit/test_paginated_collection.py
deleted file mode 100644
index 6dfef56..0000000
--- a/tests/unit/test_paginated_collection.py
+++ /dev/null
@@ -1,42 +0,0 @@
-from tests.test_helper import *
-
-from braintree.paginated_collection import PaginatedCollection
-from braintree.paginated_result import PaginatedResult
-
-class TestPaginatedCollection(unittest.TestCase):
-    def test_fetches_once_when_page_and_total_sizes_match(self):
-        def paging_function(current_page):
-            if current_page > 1:
-                raise "too many pages fetched"
-            else:
-                return PaginatedResult(1, 1, [1])
-        collection = PaginatedCollection(paging_function)
-
-        items = [i for i in collection.items]
-        self.assertEqual(1, len(items))
-
-    def test_fetches_collections_less_than_one_page(self):
-        def paging_function(current_page):
-            if current_page > 1:
-                raise "too many pages fetched"
-            else:
-                return PaginatedResult(2, 5, [1, 2])
-        collection = PaginatedCollection(paging_function)
-
-        items = [i for i in collection.items]
-        self.assertEqual(2, len(items))
-        self.assertEqual(1, items[0])
-        self.assertEqual(2, items[1])
-
-    def test_fetches_multiple_pages(self):
-        def paging_function(current_page):
-            if current_page > 2:
-                raise "too many pages fetched"
-            else:
-                return PaginatedResult(2, 1, [current_page])
-        collection = PaginatedCollection(paging_function)
-
-        items = [i for i in collection.items]
-        self.assertEqual(2, len(items))
-        self.assertEqual(1, items[0])
-        self.assertEqual(2, items[1])
diff --git a/tests/unit/test_partner_merchant.py b/tests/unit/test_partner_merchant.py
deleted file mode 100644
index cec3bfc..0000000
--- a/tests/unit/test_partner_merchant.py
+++ /dev/null
@@ -1,15 +0,0 @@
-from tests.test_helper import *
-
-class TestPartnerMerchant(unittest.TestCase):
-    def test_representation(self):
-        merchant = PartnerMerchant(None, {"partner_merchant_id": "abc123",
-                                          "private_key": "my_private_key",
-                                          "public_key": "my_public_key",
-                                          "merchant_public_id": "foobar",
-                                          "client_side_encryption_key": "cse_key"})
-        self.assertTrue("partner_merchant_id: 'abc123'" in repr(merchant))
-        self.assertTrue("public_key: 'my_public_key'" in repr(merchant))
-        self.assertTrue("merchant_public_id: 'foobar'" in repr(merchant))
-        self.assertTrue("client_side_encryption_key: 'cse_key'" in repr(merchant))
-
-        self.assertFalse("private_key: 'my_private_key'" in repr(merchant))
diff --git a/tests/unit/test_payment_method_gateway.py b/tests/unit/test_payment_method_gateway.py
deleted file mode 100644
index 85cd75f..0000000
--- a/tests/unit/test_payment_method_gateway.py
+++ /dev/null
@@ -1,168 +0,0 @@
-from tests.test_helper import *
-from braintree.payment_method_gateway import PaymentMethodGateway
-if sys.version_info[0] == 2:
-    from mock import MagicMock
-else:
-    from unittest.mock import MagicMock
-
-class TestPaymentMethodGateway(unittest.TestCase):
-    def test_create_signature(self):
-        actual_signature = PaymentMethod.signature("create")
-
-        expected_signature = [
-            "billing_address_id",
-            "cardholder_name",
-            "customer_id",
-            "cvv",
-            "device_data",
-            "device_session_id",
-            "expiration_date",
-            "expiration_month",
-            "expiration_year",
-            "number",
-            "payment_method_nonce",
-            "paypal_refresh_token",
-            "paypal_vault_without_upgrade",
-            "token",
-            {
-                "billing_address": Address.create_signature()},
-            {
-                "options": [
-                    "fail_on_duplicate_payment_method",
-                    "make_default",
-                    "us_bank_account_verification_method",
-                    "verification_merchant_account_id",
-                    "verify_card",
-                    "verification_amount",
-                    "verification_account_type",
-                    {
-                        "adyen":[
-                            "overwrite_brand",
-                            "selected_brand"
-                        ]
-                    },
-                    {
-                        "paypal":[
-                            "payee_email",
-                            "order_id",
-                            "custom_field",
-                            "description",
-                            "amount",
-                            {
-                                "shipping":[
-                                    "company",
-                                    "country_code_alpha2",
-                                    "country_code_alpha3",
-                                    "country_code_numeric",
-                                    "country_name",
-                                    "customer_id",
-                                    "extended_address",
-                                    "first_name",
-                                    "last_name",
-                                    "locality",
-                                    "postal_code",
-                                    "region",
-                                    "street_address"
-                                ]
-                            },
-                        ]
-                    },
-                ]
-            }
-        ]
-
-        self.assertEqual(expected_signature, actual_signature)
-
-    def test_update_signature(self):
-        actual_signature = PaymentMethod.update_signature()
-
-        expected_signature = [
-            "billing_address_id",
-            "cardholder_name",
-            "cvv",
-            "device_session_id",
-            "expiration_date",
-            "expiration_month",
-            "expiration_year",
-            "number",
-            "token",
-            "venmo_sdk_payment_method_code",
-            "device_data",
-            "fraud_merchant_id",
-            "payment_method_nonce",
-            {
-                "options": [
-                    "make_default",
-                    "us_bank_account_verification_method",
-                    "verify_card",
-                    "verification_amount",
-                    "verification_merchant_account_id",
-                    "verification_account_type",
-                    "venmo_sdk_session",
-                    {
-                        "adyen":[
-                            "overwrite_brand",
-                            "selected_brand"
-                        ]
-                    }
-                ]
-            },
-            {
-                "billing_address" : Address.update_signature() + [{"options": ["update_existing"]}]
-            }
-        ]
-
-        self.assertEqual(expected_signature, actual_signature)
-
-    def test_nonce_grant_params(self):
-        """
-        We validate parameters to PaymentMethod.grant properly
-        """
-        payment_method_gateway = PaymentMethodGateway(BraintreeGateway(None))
-        options = { "include_billing_postal_code": True }
-        with self.assertRaises(ValueError):
-            payment_method_gateway.grant("", options)
-
-        with self.assertRaises(ValueError):
-            payment_method_gateway.grant("\t", False)
-
-        with self.assertRaises(ValueError):
-            payment_method_gateway.grant(None, True)
-
-    def test_nonce_revoke_params(self):
-        payment_method_gateway = PaymentMethodGateway(BraintreeGateway(None))
-        with self.assertRaises(ValueError):
-            payment_method_gateway.revoke("")
-
-        with self.assertRaises(ValueError):
-            payment_method_gateway.revoke("\t")
-
-        with self.assertRaises(ValueError):
-            payment_method_gateway.revoke(None)
-
-    def test_delete_with_revoke_all_grants_value_as_true(self):
-        payment_method_gateway, http_mock  = self.setup_payment_method_gateway_and_mock_http()
-        payment_method_gateway.delete("some_token", {"revoke_all_grants": True})
-        self.assertTrue("delete('/merchants/integration_merchant_id/payment_methods/any/some_token?revoke_all_grants=true')" in str(http_mock.mock_calls))
-
-    def test_delete_with_revoke_all_grants_value_as_false(self):
-        payment_method_gateway, http_mock  = self.setup_payment_method_gateway_and_mock_http()
-        payment_method_gateway.delete("some_token", {"revoke_all_grants": False})
-        self.assertTrue("delete('/merchants/integration_merchant_id/payment_methods/any/some_token?revoke_all_grants=false')" in str(http_mock.mock_calls))
-
-    def test_delete_without_revoke_all_grants(self):
-        payment_method_gateway, http_mock  = self.setup_payment_method_gateway_and_mock_http()
-        payment_method_gateway.delete("some_token")
-        self.assertTrue("delete('/merchants/integration_merchant_id/payment_methods/any/some_token')" in str(http_mock.mock_calls)) 
-
-    def test_delete_with_invalid_keys_to_raise_error(self):
-        payment_method_gateway, http_mock  = self.setup_payment_method_gateway_and_mock_http()
-        with self.assertRaises(KeyError):
-            payment_method_gateway.delete("some_token", {"invalid_keys": False})
-
-    def setup_payment_method_gateway_and_mock_http(self):
-        braintree_gateway = BraintreeGateway(Configuration.instantiate())
-        payment_method_gateway = PaymentMethodGateway(braintree_gateway)
-        http_mock = MagicMock(name='config.http.delete')
-        braintree_gateway.config.http = http_mock
-        return payment_method_gateway, http_mock
diff --git a/tests/unit/test_payment_method_nonce.py b/tests/unit/test_payment_method_nonce.py
deleted file mode 100644
index 873bc58..0000000
--- a/tests/unit/test_payment_method_nonce.py
+++ /dev/null
@@ -1,10 +0,0 @@
-from tests.test_helper import *
-
-class TestPaymentMethodNonce(unittest.TestCase):
-    @raises(NotFoundError)
-    def test_finding_empty_id_raises_not_found_exception(self):
-        PaymentMethodNonce.find(" ")
-
-    @raises(NotFoundError)
-    def test_finding_None_id_raises_not_found_exception(self):
-        PaymentMethodNonce.find(None)
diff --git a/tests/unit/test_payment_method_parser.py b/tests/unit/test_payment_method_parser.py
deleted file mode 100644
index 69399d7..0000000
--- a/tests/unit/test_payment_method_parser.py
+++ /dev/null
@@ -1,39 +0,0 @@
-from tests.test_helper import *
-from braintree.payment_method_parser import parse_payment_method
-if sys.version_info[0] == 2:
-    from mock import MagicMock
-else:
-    from unittest.mock import MagicMock
-
-class TestPaymentMethodParser(unittest.TestCase):
-    def test_parse_response_returns_a_credit_card(self):
-        credit_card = parse_payment_method(BraintreeGateway(None), {
-            "credit_card": {"bin": "411111", "last_4": "1111"}
-        })
-
-        self.assertEqual(CreditCard, credit_card.__class__)
-        self.assertEqual("411111", credit_card.bin)
-        self.assertEqual("1111", credit_card.last_4)
-
-    def test_parse_response_returns_a_paypal_account(self):
-        paypal_account = parse_payment_method(BraintreeGateway(None), {
-            "paypal_account": {"token": "1234", "default": False}
-        })
-
-        self.assertEqual(PayPalAccount, paypal_account.__class__)
-        self.assertEqual("1234", paypal_account.token)
-        self.assertFalse(paypal_account.default)
-
-    def test_parse_response_returns_an_unknown_payment_method(self):
-        unknown_payment_method = parse_payment_method(BraintreeGateway(None), {
-            "new_fancy_payment_method": {
-                "token": "1234",
-                "default": True,
-                "other_fancy_thing": "is-shiny"
-            }
-        })
-
-        self.assertEqual(UnknownPaymentMethod, unknown_payment_method.__class__)
-        self.assertEqual("1234", unknown_payment_method.token)
-        self.assertTrue(unknown_payment_method.default)
-
diff --git a/tests/unit/test_resource.py b/tests/unit/test_resource.py
deleted file mode 100644
index 0a85af1..0000000
--- a/tests/unit/test_resource.py
+++ /dev/null
@@ -1,115 +0,0 @@
-from tests.test_helper import *
-from braintree.resource import Resource
-
-class TestResource(unittest.TestCase):
-    def test_verify_keys_allows_wildcard_keys(self):
-        signature = [
-            {"foo": [{"bar": ["__any_key__"]}]}
-        ]
-        params = {
-            "foo[bar][lower]": "lowercase",
-            "foo[bar][UPPER]": "uppercase",
-            "foo[bar][123]": "numeric",
-            "foo[bar][under_scores]": "underscores",
-            "foo[bar][dash-es]": "dashes",
-            "foo[bar][ABC-abc_123]": "all together"
-        }
-        Resource.verify_keys(params, signature)
-
-    @raises(KeyError)
-    def test_verify_keys_escapes_brackets_in_signature(self):
-        signature = [
-            {"customer": [{"custom_fields": ["__any_key__"]}]}
-        ]
-        params = {
-            "customer_id": "value",
-        }
-        Resource.verify_keys(params, signature)
-
-    def test_verify_keys_works_with_array_param(self):
-        signature = [
-            {"customer": ["one", "two"]}
-        ]
-        params = {
-            "customer": {
-                "one": "foo"
-            }
-        }
-        Resource.verify_keys(params, signature)
-
-    @raises(KeyError)
-    def test_verify_keys_raises_on_bad_array_param(self):
-        signature = [
-            {"customer": ["one", "two"]}
-        ]
-        params = {
-            "customer": {
-                "invalid": "foo"
-            }
-        }
-        Resource.verify_keys(params, signature)
-
-    def test_verify_keys_works_with_arrays(self):
-        signature = [
-            {"add_ons": [{"update": ["existing_id", "quantity"]}]}
-        ]
-        params = {
-            "add_ons": {
-                "update": [
-                    {
-                        "existing_id": "foo",
-                        "quantity": 10
-                    }
-                ]
-            }
-        }
-        Resource.verify_keys(params, signature)
-
-    @raises(KeyError)
-    def test_verify_keys_raises_with_invalid_param_in_arrays(self):
-        signature = [
-            {"add_ons": [{"update": ["existing_id", "quantity"]}]}
-        ]
-        params = {
-            "add_ons": {
-                "update": [
-                    {
-                        "invalid": "foo",
-                        "quantity": 10
-                    }
-                ]
-            }
-        }
-        Resource.verify_keys(params, signature)
-
-    def test_verify_keys_allows_text(self):
-        text_string = u"text_string"
-        assert isinstance(text_string, TestHelper.text_type)
-
-        signature = [
-            {"customer": [{"custom_fields": [text_string]}]}
-        ]
-        params = {
-            "customer": {
-                "custom_fields": {
-                    text_string : text_string
-                }
-            }
-        }
-        Resource.verify_keys(params, signature)
-
-    def test_verify_keys_allows_raw_data(self):
-        raw_string = str.encode("raw_string")
-        assert isinstance(raw_string, TestHelper.raw_type)
-
-        signature = [
-            {"customer": [{"custom_fields": [raw_string]}]}
-        ]
-        params = {
-            "customer": {
-                "custom_fields": {
-                    raw_string : raw_string
-                }
-            }
-        }
-        Resource.verify_keys(params, signature)
diff --git a/tests/unit/test_resource_collection.py b/tests/unit/test_resource_collection.py
deleted file mode 100644
index e90c241..0000000
--- a/tests/unit/test_resource_collection.py
+++ /dev/null
@@ -1,51 +0,0 @@
-from tests.test_helper import *
-
-class TestResourceCollection(unittest.TestCase):
-    collection_data = {
-        "search_results": {
-            "page_size": 2,
-            "ids": ["0", "1", "2", "3", "4"]
-        }
-    }
-
-    class TestResource:
-        items = ["a", "b", "c", "d", "e"]
-
-        @staticmethod
-        def fetch(_, ids):
-            return [TestResourceCollection.TestResource.items[int(resource_id)] for resource_id in ids]
-
-    def test_iterating_over_contents(self):
-        collection = ResourceCollection("some_query", self.collection_data, TestResourceCollection.TestResource.fetch)
-        new_items = []
-        index = 0
-        for item in collection.items:
-            self.assertEqual(TestResourceCollection.TestResource.items[index], item)
-            new_items.append(item)
-            index += 1
-
-        self.assertEqual(5, len(new_items))
-
-    def test_iterate_using_iterator_protocol(self):
-        collection = ResourceCollection("some_query", self.collection_data, TestResourceCollection.TestResource.fetch)
-        for test_elem, coll_elem in zip(self.TestResource.items, collection):
-            self.assertEqual(test_elem, coll_elem)
-
-    def test_ids_returns_array_of_ids(self):
-        collection = ResourceCollection("some_query", self.collection_data, TestResourceCollection.TestResource.fetch)
-        self.assertEqual(collection.ids, self.collection_data['search_results']['ids'])
-
-    def test_ids_returns_array_of_empty_ids(self):
-        empty_collection_data = {
-            "search_results": {
-                "page_size": 2,
-                "ids": []
-             }
-         }
-        collection = ResourceCollection("some_query", empty_collection_data, TestResourceCollection.TestResource.fetch)
-        self.assertEqual(collection.ids, [])
-
-    @raises_with_regexp(UnexpectedError, "Unprocessable entity due to an invalid request")
-    def test_no_search_results(self):
-        bad_collection_data = {}
-        ResourceCollection("some_query", bad_collection_data, TestResourceCollection.TestResource.fetch)
diff --git a/tests/unit/test_risk_data.py b/tests/unit/test_risk_data.py
deleted file mode 100644
index 0fa32ed..0000000
--- a/tests/unit/test_risk_data.py
+++ /dev/null
@@ -1,10 +0,0 @@
-from tests.test_helper import *
-from braintree import *
-
-class TestRiskData(unittest.TestCase):
-    def test_initialization_of_attributes(self):
-        risk_data = RiskData({"id": "123", "decision": "Unknown", "device_data_captured": True, "fraud_service_provider": "some_fraud_provider"})
-        self.assertEqual("123", risk_data.id)
-        self.assertEqual("Unknown", risk_data.decision)
-        self.assertEqual(True, risk_data.device_data_captured)
-        self.assertEqual("some_fraud_provider", risk_data.fraud_service_provider)
diff --git a/tests/unit/test_search.py b/tests/unit/test_search.py
deleted file mode 100644
index 8ec5c2a..0000000
--- a/tests/unit/test_search.py
+++ /dev/null
@@ -1,120 +0,0 @@
-from tests.test_helper import *
-
-class TestSearch(unittest.TestCase):
-    def test_text_node_is(self):
-        node = Search.TextNodeBuilder("name")
-        self.assertEqual({"is": "value"}, (node == "value").to_param())
-
-    def test_text_node_is_not(self):
-        node = Search.TextNodeBuilder("name")
-        self.assertEqual({"is_not": "value"}, (node != "value").to_param())
-
-    def test_text_node_starts_with(self):
-        node = Search.TextNodeBuilder("name")
-        self.assertEqual({"starts_with": "value"}, (node.starts_with("value")).to_param())
-
-    def test_text_node_ends_with(self):
-        node = Search.TextNodeBuilder("name")
-        self.assertEqual({"ends_with": "value"}, (node.ends_with("value")).to_param())
-
-    def test_text_node_contains(self):
-        node = Search.TextNodeBuilder("name")
-        self.assertEqual({"contains": "value"}, (node.contains("value")).to_param())
-
-    def test_multiple_value_node_in_list(self):
-        node = Search.MultipleValueNodeBuilder("name")
-        self.assertEqual(["value1", "value2"], (node.in_list(["value1", "value2"])).to_param())
-
-    def test_multiple_value_node_in_list_as_arg_list(self):
-        node = Search.MultipleValueNodeBuilder("name")
-        self.assertEqual(["value1", "value2"], (node.in_list("value1", "value2")).to_param())
-
-    def test_multiple_value_node_is(self):
-        node = Search.MultipleValueNodeBuilder("name")
-        self.assertEqual(["value1"], (node == "value1").to_param())
-
-    def test_multiple_value_node_with_value_in_whitelist(self):
-        node = Search.MultipleValueNodeBuilder("name", ["okay"])
-        self.assertEqual(["okay"], (node == "okay").to_param())
-
-    @raises(AttributeError)
-    def test_multiple_value_node_with_value_not_in_whitelist(self):
-        node = Search.MultipleValueNodeBuilder("name", ["okay", "also okay"])
-        node == "not okay"
-
-    def test_multiple_value_or_text_node_is(self):
-        node = Search.MultipleValueOrTextNodeBuilder("name")
-        self.assertEqual({"is": "value"}, (node == "value").to_param())
-
-    def test_multiple_value_or_text_node_is_not(self):
-        node = Search.MultipleValueOrTextNodeBuilder("name")
-        self.assertEqual({"is_not": "value"}, (node != "value").to_param())
-
-    def test_multiple_value_or_text_node_starts_with(self):
-        node = Search.MultipleValueOrTextNodeBuilder("name")
-        self.assertEqual({"starts_with": "value"}, (node.starts_with("value")).to_param())
-
-    def test_multiple_value_or_text_node_ends_with(self):
-        node = Search.MultipleValueOrTextNodeBuilder("name")
-        self.assertEqual({"ends_with": "value"}, (node.ends_with("value")).to_param())
-
-    def test_multiple_value_or_text_node_contains(self):
-        node = Search.MultipleValueOrTextNodeBuilder("name")
-        self.assertEqual({"contains": "value"}, (node.contains("value")).to_param())
-
-    def test_multiple_value_or_text_node_in_list(self):
-        node = Search.MultipleValueOrTextNodeBuilder("name")
-        self.assertEqual(["value1", "value2"], (node.in_list(["value1", "value2"])).to_param())
-
-    def test_multiple_value_or_text_node_in_list_as_arg_list(self):
-        node = Search.MultipleValueOrTextNodeBuilder("name")
-        self.assertEqual(["value1", "value2"], (node.in_list("value1", "value2")).to_param())
-
-    def test_multiple_value_or_text_node_with_value_in_whitelist(self):
-        node = Search.MultipleValueOrTextNodeBuilder("name", ["okay"])
-        self.assertEqual(["okay"], node.in_list("okay").to_param())
-
-    @raises(AttributeError)
-    def test_multiple_value_or_text_node_with_value_not_in_whitelist(self):
-        node = Search.MultipleValueOrTextNodeBuilder("name", ["okay"])
-        node.in_list("not okay").to_param()
-
-    def test_range_node_min_ge(self):
-        node = Search.RangeNodeBuilder("name")
-        self.assertEqual({"min": "value"}, (node >= "value").to_param())
-
-    def test_range_node_min_greater_than_or_equal_to(self):
-        node = Search.RangeNodeBuilder("name")
-        self.assertEqual({"min": "value"}, (node.greater_than_or_equal_to("value")).to_param())
-
-    def test_range_node_max_le(self):
-        node = Search.RangeNodeBuilder("name")
-        self.assertEqual({"max": "value"}, (node <= "value").to_param())
-
-    def test_range_node_max_less_than_or_equal_to(self):
-        node = Search.RangeNodeBuilder("name")
-        self.assertEqual({"max": "value"}, (node.less_than_or_equal_to("value")).to_param())
-
-    def test_range_node_between(self):
-        node = Search.RangeNodeBuilder("name")
-        self.assertEqual({"min": "min_value", "max": "max_value"}, (node.between("min_value", "max_value")).to_param())
-
-    def test_range_node_is(self):
-        node = Search.RangeNodeBuilder("name")
-        self.assertEqual({"is": "value"}, (node == "value").to_param())
-
-    def test_key_value_node_is_eq(self):
-        node = Search.KeyValueNodeBuilder("name")
-        self.assertTrue((node == True).to_param())
-
-    def test_key_value_node_is_equal(self):
-        node = Search.KeyValueNodeBuilder("name")
-        self.assertEqual(True, (node.is_equal(True)).to_param())
-
-    def test_key_value_node_is_not_equal(self):
-        node = Search.KeyValueNodeBuilder("name")
-        self.assertEqual(False, (node.is_not_equal(True)).to_param())
-
-    def test_key_value_node_symbols_is_not_equal(self):
-        node = Search.KeyValueNodeBuilder("name")
-        self.assertEqual(False, (node != True).to_param())
diff --git a/tests/unit/test_setup.py b/tests/unit/test_setup.py
deleted file mode 100644
index 6968e22..0000000
--- a/tests/unit/test_setup.py
+++ /dev/null
@@ -1,21 +0,0 @@
-from tests.test_helper import *
-import os
-
-class TestSetup(unittest.TestCase):
-    def test_packages_includes_all_packages(self):
-        with open('setup.py', 'r') as f:
-            setup_contents = f.read()
-        packages_line = re.findall('packages=.*', setup_contents)
-        packages_from_setup = re.findall('"(.*?)"', str(packages_line))
-
-        packages_from_directories = ['braintree']
-        directories_that_dont_have_packages = ['braintree.ssl']
-        for dirname, dirnames, _ in os.walk('braintree'):
-            for subdirname in dirnames:
-                package_from_directory = re.sub('/', '.', os.path.join(dirname, subdirname))
-                if package_from_directory not in directories_that_dont_have_packages and subdirname != '__pycache__':
-                    packages_from_directories.append(package_from_directory)
-
-        mismatch_message = "List of packages in setup.py doesn't match subdirectories of 'braintree' - " \
-                + "add your new directory to 'packages, or if none, `git clean -df` to remove a stale directory"
-        self.assertEqual(sorted(packages_from_directories), sorted(packages_from_setup), mismatch_message)
diff --git a/tests/unit/test_signature_service.py b/tests/unit/test_signature_service.py
deleted file mode 100644
index 15a3655..0000000
--- a/tests/unit/test_signature_service.py
+++ /dev/null
@@ -1,14 +0,0 @@
-from tests.test_helper import *
-
-class FakeDigest(object):
-
-    @staticmethod
-    def hmac_hash(key, data):
-        return "%s_signed_with_%s" % (data, key)
-
-class TestSignatureService(unittest.TestCase):
-
-    def test_hashes_with_digest(self):
-        signature_service = SignatureService("fake_key", FakeDigest.hmac_hash)
-        signed = signature_service.sign({"foo": "bar"})
-        self.assertEqual("foo=bar_signed_with_fake_key|foo=bar", signed)
diff --git a/tests/unit/test_subscription.py b/tests/unit/test_subscription.py
deleted file mode 100644
index 0c14fb5..0000000
--- a/tests/unit/test_subscription.py
+++ /dev/null
@@ -1,18 +0,0 @@
-from tests.test_helper import *
-
-class TestSubscription(unittest.TestCase):
-    @raises_with_regexp(KeyError, "'Invalid keys: bad_key'")
-    def test_create_raises_exception_with_bad_keys(self):
-        Subscription.create({"bad_key": "value"})
-
-    @raises_with_regexp(KeyError, "'Invalid keys: bad_key'")
-    def test_update_raises_exception_with_bad_keys(self):
-        Subscription.update("id", {"bad_key": "value"})
-
-    @raises(NotFoundError)
-    def test_finding_empty_id_raises_not_found_exception(self):
-        Subscription.find(" ")
-
-    @raises(NotFoundError)
-    def test_finding_None_id_raises_not_found_exception(self):
-        Subscription.find(None)
diff --git a/tests/unit/test_subscription_search.py b/tests/unit/test_subscription_search.py
deleted file mode 100644
index 31ec078..0000000
--- a/tests/unit/test_subscription_search.py
+++ /dev/null
@@ -1,49 +0,0 @@
-from tests.test_helper import *
-
-class TestSubscriptionSearch(unittest.TestCase):
-    def test_billing_cycles_remaining_is_a_range_node(self):
-        self.assertEqual(Search.RangeNodeBuilder, type(SubscriptionSearch.billing_cycles_remaining))
-
-    def test_created_at_is_a_range_node(self):
-        self.assertEqual(Search.RangeNodeBuilder, type(SubscriptionSearch.created_at))
-
-    def test_days_past_due_is_a_range_node(self):
-        self.assertEqual(Search.RangeNodeBuilder, type(SubscriptionSearch.days_past_due))
-
-    def test_id_is_a_text_node(self):
-        self.assertEqual(Search.TextNodeBuilder, type(SubscriptionSearch.id))
-
-    def test_merchant_account_id_is_a_multiple_value_node(self):
-        self.assertEqual(Search.MultipleValueNodeBuilder, type(SubscriptionSearch.merchant_account_id))
-
-    def test_plan_id_is_a_multiple_value_or_text_node(self):
-        self.assertEqual(Search.MultipleValueOrTextNodeBuilder, type(SubscriptionSearch.plan_id))
-
-    def test_price_is_a_range_node(self):
-        self.assertEqual(Search.RangeNodeBuilder, type(SubscriptionSearch.price))
-
-    def test_status_is_a_multiple_value_node(self):
-        self.assertEqual(Search.MultipleValueNodeBuilder, type(SubscriptionSearch.status))
-
-    def test_in_trial_period_is_multiple_value_node(self):
-        self.assertEqual(Search.MultipleValueNodeBuilder, type(SubscriptionSearch.in_trial_period))
-
-    def test_status_whitelist(self):
-        SubscriptionSearch.status.in_list(
-            Subscription.Status.Active,
-            Subscription.Status.Canceled,
-            Subscription.Status.Expired,
-            Subscription.Status.PastDue
-        )
-
-    @raises(AttributeError)
-    def test_status_not_in_whitelist(self):
-        SubscriptionSearch.status.in_list(
-            Subscription.Status.Active,
-            Subscription.Status.Canceled,
-            Subscription.Status.Expired,
-            "not a status"
-        )
-
-    def test_ids_is_a_multiple_value_node(self):
-        self.assertEqual(Search.MultipleValueNodeBuilder, type(SubscriptionSearch.ids))
diff --git a/tests/unit/test_successful_result.py b/tests/unit/test_successful_result.py
deleted file mode 100644
index c3a0284..0000000
--- a/tests/unit/test_successful_result.py
+++ /dev/null
@@ -1,9 +0,0 @@
-from tests.test_helper import *
-
-class TestSuccessfulResult(unittest.TestCase):
-    def test_is_success(self):
-        self.assertTrue(SuccessfulResult({}).is_success)
-
-    def test_attributes_are_exposed(self):
-        result = SuccessfulResult({"name": "drew"})
-        self.assertEqual("drew", result.name)
diff --git a/tests/unit/test_three_d_secure_info.py b/tests/unit/test_three_d_secure_info.py
deleted file mode 100644
index 85f7306..0000000
--- a/tests/unit/test_three_d_secure_info.py
+++ /dev/null
@@ -1,26 +0,0 @@
-from tests.test_helper import *
-from braintree import *
-
-class TestThreeDSecureInfo(unittest.TestCase):
-    def test_initialization_of_attributes(self):
-        three_d_secure_info = ThreeDSecureInfo({
-            "enrolled": "Y",
-            "status": "authenticate_successful",
-            "liability_shifted": True,
-            "liability_shift_possible": True,
-            "cavv": "some_cavv",
-            "xid": "some_xid",
-            "ds_transaction_id": "some_ds_txn_id",
-            "eci_flag": "07",
-            "three_d_secure_version": "1.0.2",
-        })
-
-        self.assertEqual("Y", three_d_secure_info.enrolled)
-        self.assertEqual("authenticate_successful", three_d_secure_info.status)
-        self.assertEqual(True, three_d_secure_info.liability_shifted)
-        self.assertEqual(True, three_d_secure_info.liability_shift_possible)
-        self.assertEqual("some_cavv", three_d_secure_info.cavv)
-        self.assertEqual("some_xid", three_d_secure_info.xid)
-        self.assertEqual("some_ds_txn_id", three_d_secure_info.ds_transaction_id)
-        self.assertEqual("07", three_d_secure_info.eci_flag)
-        self.assertEqual("1.0.2", three_d_secure_info.three_d_secure_version)
diff --git a/tests/unit/test_transaction.py b/tests/unit/test_transaction.py
deleted file mode 100644
index dfd7dba..0000000
--- a/tests/unit/test_transaction.py
+++ /dev/null
@@ -1,243 +0,0 @@
-from tests.test_helper import *
-from braintree.test.credit_card_numbers import CreditCardNumbers
-from datetime import datetime
-from datetime import date
-from braintree.authorization_adjustment import AuthorizationAdjustment
-if sys.version_info[0] == 2:
-    from mock import MagicMock
-else:
-    from unittest.mock import MagicMock
-
-class TestTransaction(unittest.TestCase):
-    @raises_with_regexp(KeyError, "'Invalid keys: bad_key'")
-    def test_clone_transaction_raises_exception_with_bad_keys(self):
-        Transaction.clone_transaction("an id", {"bad_key": "value"})
-
-    @raises_with_regexp(KeyError, "'Invalid keys: bad_key'")
-    def test_sale_raises_exception_with_bad_keys(self):
-        Transaction.sale({"bad_key": "value"})
-
-    @raises_with_regexp(KeyError, "'Invalid keys: credit_card\[bad_key\]'")
-    def test_sale_raises_exception_with_nested_bad_keys(self):
-        Transaction.sale({"credit_card": {"bad_key": "value"}})
-
-    @raises_with_regexp(KeyError, "'Invalid keys: bad_key'")
-    def test_tr_data_for_sale_raises_error_with_bad_keys(self):
-        Transaction.tr_data_for_sale({"bad_key": "value"}, "http://example.com")
-
-    @raises(NotFoundError)
-    def test_finding_empty_id_raises_not_found_exception(self):
-        Transaction.find(" ")
-
-    @raises(NotFoundError)
-    def test_finding_none_raises_not_found_exception(self):
-        Transaction.find(None)
-
-    def test_constructor_includes_disbursement_information(self):
-        attributes = {
-            'amount': '27.00',
-            'tax_amount': '1.00',
-            'customer_id': '4096',
-            'merchant_account_id': '8192',
-            'order_id': '106601',
-            'channel': '101',
-            'payment_method_token': 'sometoken',
-            'purchase_order_number': '20202',
-            'recurring': 'False',
-            'disbursement_details': {
-                'settlement_amount': '27.00',
-                'settlement_currency_iso_code': 'USD',
-                'settlement_currency_exchange_rate': '1',
-                'disbursement_date': date(2013, 4, 10),
-                'funds_held': False
-            }
-        }
-
-        transaction = Transaction(None, attributes)
-
-        self.assertEqual(transaction.disbursement_details.settlement_amount, Decimal('27.00'))
-        self.assertEqual(transaction.disbursement_details.settlement_currency_iso_code, 'USD')
-        self.assertEqual(transaction.disbursement_details.settlement_currency_exchange_rate, Decimal('1'))
-        self.assertEqual(transaction.disbursement_details.disbursement_date, date(2013, 4, 10))
-        self.assertEqual(transaction.disbursement_details.funds_held, False)
-        self.assertEqual(transaction.is_disbursed, True)
-
-    def test_transaction_handles_nil_risk_data(self):
-        attributes = {
-            'amount': '27.00',
-            'tax_amount': '1.00',
-            'customer_id': '4096',
-            'merchant_account_id': '8192',
-            'order_id': '106601',
-            'channel': '101',
-            'payment_method_token': 'sometoken',
-            'purchase_order_number': '20202',
-            'recurring': 'False',
-        }
-
-        transaction = Transaction(None, attributes)
-
-        self.assertEqual(transaction.risk_data, None)
-
-    def test_is_disbursed_false(self):
-        attributes = {
-            'amount': '27.00',
-            'tax_amount': '1.00',
-            'customer_id': '4096',
-            'merchant_account_id': '8192',
-            'order_id': '106601',
-            'channel': '101',
-            'payment_method_token': 'sometoken',
-            'purchase_order_number': '20202',
-            'recurring': 'False',
-            'disbursement_details': {
-                'settlement_amount': None,
-                'settlement_currency_iso_code': None,
-                'settlement_currency_exchange_rate': None,
-                'disbursement_date': None,
-                'funds_held': None,
-            }
-        }
-
-        transaction = Transaction(None, attributes)
-
-        self.assertEqual(transaction.is_disbursed, False)
-
-    def test_sale_with_skip_advanced_fraud_checking_value_as_true(self):
-        attributes = {
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": CreditCardNumbers.Visa,
-                "expiration_date": "05/2009"
-            },
-            "options": {
-                "skip_advanced_fraud_checking": True
-            }
-        }
-
-        transaction_gateway = self.setup_transaction_gateway_and_mock_post()
-        transaction_gateway.sale(attributes)
-        transaction_param = transaction_gateway._post.call_args[0][1]
-        self.assertTrue(transaction_param['transaction']['options']['skip_advanced_fraud_checking'])
-
-    def test_sale_with_skip_advanced_fraud_checking_value_as_false(self):
-        attributes = {
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": CreditCardNumbers.Visa,
-                "expiration_date": "05/2009"
-            },
-            "options": {
-                "skip_advanced_fraud_checking": False
-            }
-        }
-
-        transaction_gateway = self.setup_transaction_gateway_and_mock_post()
-        transaction_gateway.sale(attributes)
-        transaction_param = transaction_gateway._post.call_args[0][1]
-        self.assertFalse(transaction_param['transaction']['options']['skip_advanced_fraud_checking'])
-
-    def test_sale_without_skip_advanced_fraud_checking_value_option(self):
-        attributes = {
-            "amount": TransactionAmounts.Authorize,
-            "credit_card": {
-                "number": CreditCardNumbers.Visa,
-                "expiration_date": "05/2009"
-            },
-            "options": {
-                "submit_for_settlement": True
-            }
-        }
-
-        transaction_gateway = self.setup_transaction_gateway_and_mock_post()
-        transaction_gateway.sale(attributes)
-        transaction_param = transaction_gateway._post.call_args[0][1]
-        self.assertTrue('skip_advanced_fraud_checking' not in transaction_param['transaction']['options'])
-
-    def setup_transaction_gateway_and_mock_post(self):
-        transaction_gateway = TransactionGateway(BraintreeGateway(None))
-        transaction_gateway._post = MagicMock(name='config.http.post')
-        return transaction_gateway
-
-    def test_ideal_payment_details(self):
-        attributes = {
-            'amount': '27.00',
-            'tax_amount': '1.00',
-            'ideal_payment': {
-                'ideal_payment_id': 'idealpayment_abc_123',
-                'masked_iban': '12************7890',
-                'bic': 'RABONL2U',
-                'image_url': 'http://www.example.com/ideal.png',
-            },
-        }
-
-        transaction = Transaction(None, attributes)
-
-        self.assertEqual(transaction.ideal_payment_details.ideal_payment_id, 'idealpayment_abc_123')
-        self.assertEqual(transaction.ideal_payment_details.masked_iban, '12************7890')
-        self.assertEqual(transaction.ideal_payment_details.bic, 'RABONL2U')
-        self.assertEqual(transaction.ideal_payment_details.image_url, 'http://www.example.com/ideal.png')
-
-
-    def test_constructor_doesnt_includes_auth_adjustments(self):
-        attributes = {
-            'amount': '27.00',
-            'customer_id': '4096',
-            'merchant_account_id': '8192',
-            'payment_method_token': 'sometoken',
-            'purchase_order_number': '20202',
-            'recurring': 'False',
-            'tax_amount': '1.00',
-        }
-
-        transaction = Transaction(None, attributes)
-        self.assertFalse(hasattr(transaction, 'authorization_adjustments'))
-
-    def test_constructor_includes_auth_adjustments(self):
-        attributes = {
-            'amount': '27.00',
-            'customer_id': '4096',
-            'merchant_account_id': '8192',
-            'payment_method_token': 'sometoken',
-            'purchase_order_number': '20202',
-            'recurring': 'False',
-            'tax_amount': '1.00',
-            'authorization_adjustments': [{
-                "amount": "20.00",
-                "timestamp": datetime(2017, 7, 12, 1, 2, 3),
-                "success": True,
-                "processor_response_code": "1000",
-                "processor_response_text": "Approved",
-            }],
-        }
-
-        transaction = Transaction(None, attributes)
-        transaction_adjustment = transaction.authorization_adjustments[0]
-        self.assertEqual(transaction_adjustment.amount, Decimal("20.00"))
-        self.assertEqual(transaction_adjustment.timestamp, datetime(2017, 7, 12, 1, 2, 3))
-        self.assertEqual(transaction_adjustment.success, True)
-        self.assertEqual(transaction_adjustment.processor_response_code, "1000")
-        self.assertEqual(transaction_adjustment.processor_response_text, "Approved")
-
-    def test_constructor_includes_network_transaction_id(self):
-        attributes = {
-            'amount': '27.00',
-            'tax_amount': '1.00',
-            'network_transaction_id': '123456789012345'
-        }
-
-        transaction = Transaction(None, attributes)
-        self.assertEqual(transaction.network_transaction_id, "123456789012345")
-
-    def test_constructor_includes_network_transaction_id(self):
-        attributes = {
-            'amount': '27.00',
-            'tax_amount': '1.00',
-            'network_response_code': '00',
-            'network_response_text': 'Successful approval/completion or V.I.P. PIN verification is successful'
-        }
-
-        transaction = Transaction(None, attributes)
-        self.assertEqual(transaction.network_response_code, "00")
-        self.assertEqual(transaction.network_response_text, "Successful approval/completion or V.I.P. PIN verification is successful")
-
diff --git a/tests/unit/test_transparent_redirect.py b/tests/unit/test_transparent_redirect.py
deleted file mode 100644
index ba84fc9..0000000
--- a/tests/unit/test_transparent_redirect.py
+++ /dev/null
@@ -1,56 +0,0 @@
-from tests.test_helper import *
-
-class TestTransparentRedirect(unittest.TestCase):
-    def test_tr_data(self):
-        data = TransparentRedirect.tr_data({"key": "val"}, "http://example.com/path?foo=bar")
-        self.__assert_valid_tr_data(data)
-
-    def __assert_valid_tr_data(self, data):
-        test_hash, content = data.split("|", 1)
-        self.assertEqual(test_hash, Crypto.sha1_hmac_hash(Configuration.private_key, content))
-
-    @raises(ForgedQueryStringError)
-    def test_parse_and_validate_query_string_raises_for_invalid_hash(self):
-        Configuration.gateway().transparent_redirect._parse_and_validate_query_string(
-            "http_status=200&id=7kdj469tw7yck32j&hash=99c9ff20cd7910a1c1e793ff9e3b2d15586dc6b9"
-        )
-
-    @raises(AuthenticationError)
-    def test_parse_and_validate_query_string_raises_for_http_status_401(self):
-        Configuration.gateway().transparent_redirect._parse_and_validate_query_string(
-            "http_status=401&id=6kdj469tw7yck32j&hash=5a26e3cde5ebedb0ec1ba8d35724360334fbf419"
-        )
-
-    @raises(AuthorizationError)
-    def test_parse_and_validate_query_string_raises_for_http_status_403(self):
-        Configuration.gateway().transparent_redirect._parse_and_validate_query_string(
-            "http_status=403&id=6kdj469tw7yck32j&hash=126d5130b71a4907e460fad23876ed70dd41dcd2"
-        )
-
-    @raises(NotFoundError)
-    def test_parse_and_validate_query_string_raises_for_http_status_404(self):
-        Configuration.gateway().transparent_redirect._parse_and_validate_query_string(
-            "http_status=404&id=6kdj469tw7yck32j&hash=0d3724a45cf1cda5524aa68f1f28899d34d2ff3a"
-        )
-
-    @raises(ServerError)
-    def test_parse_and_validate_query_string_raises_for_http_status_500(self):
-        Configuration.gateway().transparent_redirect._parse_and_validate_query_string(
-            "http_status=500&id=6kdj469tw7yck32j&hash=a839a44ca69d59a3d6f639c294794989676632dc"
-        )
-
-    @raises(DownForMaintenanceError)
-    def test_parse_and_validate_query_string_raises_for_http_status_503(self):
-        Configuration.gateway().transparent_redirect._parse_and_validate_query_string(
-            "http_status=503&id=6kdj469tw7yck32j&hash=1b3d29199a282e63074a7823b76bccacdf732da6"
-        )
-
-    @raises(UnexpectedError)
-    def test_parse_and_validate_query_string_raises_for_unexpected_http_status(self):
-        Configuration.gateway().transparent_redirect._parse_and_validate_query_string(
-            "http_status=600&id=6kdj469tw7yck32j&hash=740633356f93384167d887de0c1d9745e3de8fb6"
-        )
-
-    def test_api_version(self):
-        data = TransparentRedirect.tr_data({"key": "val"}, "http://example.com/path?foo=bar")
-        self.assertTrue("api_version=5" in data)
diff --git a/tests/unit/test_unknown_payment_method.py b/tests/unit/test_unknown_payment_method.py
deleted file mode 100644
index b86b30d..0000000
--- a/tests/unit/test_unknown_payment_method.py
+++ /dev/null
@@ -1,6 +0,0 @@
-from tests.test_helper import *
-
-class TestUnknownPaymentMethod(unittest.TestCase):
-    def test_image_url(self):
-        unknown_payment_method = UnknownPaymentMethod("gateway", {"token": "TOKEN"})
-        self.assertEqual("https://assets.braintreegateway.com/payment_method_logo/unknown.png", unknown_payment_method.image_url())
diff --git a/tests/unit/test_us_bank_account.py b/tests/unit/test_us_bank_account.py
deleted file mode 100644
index 74b629c..0000000
--- a/tests/unit/test_us_bank_account.py
+++ /dev/null
@@ -1,50 +0,0 @@
-from tests.test_helper import *
-from datetime import date
-from braintree.us_bank_account import UsBankAccount
-from braintree.us_bank_account_verification import UsBankAccountVerification
-
-class TestUsBankAccount(unittest.TestCase):
-    def test_constructor(self):
-        attributes = {
-            "last_four": "1234",
-            "routing_number": "55555",
-            "account_type": "fake-account",
-            "account_holder_name": "John Doe",
-            "token": "7777-7777",
-            "image_url": "some.png",
-            "bank_name": "Chase",
-            "ach_mandate": None,
-        }
-
-        us_bank_account = UsBankAccount({}, attributes)
-        self.assertEqual(us_bank_account.last_four, "1234")
-        self.assertEqual(us_bank_account.routing_number, "55555")
-        self.assertEqual(us_bank_account.account_type, "fake-account")
-        self.assertEqual(us_bank_account.account_holder_name, "John Doe")
-        self.assertEqual(us_bank_account.token, "7777-7777")
-        self.assertEqual(us_bank_account.image_url, "some.png")
-        self.assertEqual(us_bank_account.bank_name, "Chase")
-        self.assertEqual(us_bank_account.ach_mandate, None)
-
-        attributes["ach_mandate"] = {"text":"Some mandate", "accepted_at": date(2013, 4, 10)}
-        us_bank_account_mandated = UsBankAccount({}, attributes)
-        self.assertEqual(us_bank_account_mandated.ach_mandate.text, "Some mandate")
-        self.assertEqual(us_bank_account_mandated.ach_mandate.accepted_at, date(2013, 4, 10))
-
-    def test_converts_verifications_to_objects(self):
-        attributes = {
-            "verifications": [
-                {
-                    "status": "verified",
-                    "verification_method": "network_check",
-                },
-            ],
-        }
-
-        us_bank_account = UsBankAccount({}, attributes)
-        self.assertEqual(len(us_bank_account.verifications), 1)
-
-        verification = us_bank_account.verifications[0]
-
-        self.assertEqual(verification.status, UsBankAccountVerification.Status.Verified)
-        self.assertEqual(verification.verification_method, UsBankAccountVerification.VerificationMethod.NetworkCheck)
diff --git a/tests/unit/test_us_bank_account_verification.py b/tests/unit/test_us_bank_account_verification.py
deleted file mode 100644
index f51bdc7..0000000
--- a/tests/unit/test_us_bank_account_verification.py
+++ /dev/null
@@ -1,38 +0,0 @@
-from datetime import datetime
-from tests.test_helper import *
-
-from braintree.us_bank_account_verification import UsBankAccountVerification
-
-class TestUsBankAccountVerification(unittest.TestCase):
-    @raises(NotFoundError)
-    def test_finding_empty_id_raises_not_found_exception(self):
-        UsBankAccountVerification.find(" ")
-
-    @raises(NotFoundError)
-    def test_finding_none_raises_not_found_exception(self):
-        UsBankAccountVerification.find(None)
-
-    def test_attributes(self):
-        attributes = {
-            "id": "my_favorite_id",
-            "status": "verified",
-            "verification_method": "independent_check",
-            "verification_determined_at": datetime(2018, 11, 11, 23, 59, 59),
-            "us_bank_account": {
-                "token": "abc123",
-                "last_4": 9999,
-            }
-        }
-
-        verification = UsBankAccountVerification({}, attributes)
-
-        self.assertEqual(verification.id, "my_favorite_id")
-        self.assertEqual(verification.status, UsBankAccountVerification.Status.Verified)
-        self.assertEqual(verification.verification_determined_at, datetime(2018, 11, 11, 23, 59, 59))
-        self.assertEqual(
-            verification.verification_method,
-            UsBankAccountVerification.VerificationMethod.IndependentCheck
-        )
-
-        self.assertEqual(verification.us_bank_account.token, "abc123")
-        self.assertEqual(verification.us_bank_account.last_4, 9999)
diff --git a/tests/unit/test_validation_error_collection.py b/tests/unit/test_validation_error_collection.py
deleted file mode 100644
index 4a06c9e..0000000
--- a/tests/unit/test_validation_error_collection.py
+++ /dev/null
@@ -1,115 +0,0 @@
-from tests.test_helper import *
-
-class TestValidationErrorCollection(unittest.TestCase):
-    def test_it_builds_an_array_of_errors_given_an_array_of_hashes(self):
-        test_hash = {"errors": [{"attribute": "some model attribute", "code": 1, "message": "bad juju"}]}
-        errors = ValidationErrorCollection(test_hash)
-        error = errors[0]
-        self.assertEqual("some model attribute", error.attribute)
-        self.assertEqual(1, error.code)
-        self.assertEqual("bad juju", error.message)
-
-    def test_for_object_provides_access_to_nested_attributes(self):
-        test_hash = {
-            "errors": [{"attribute": "some model attribute", "code": 1, "message": "bad juju"}],
-            "nested": {
-                "errors": [{"attribute": "number", "code": 2, "message": "badder juju"}]
-            }
-        }
-        errors = ValidationErrorCollection(test_hash)
-        error = errors.for_object("nested").on("number")[0]
-
-        self.assertEqual("number", error.attribute)
-        self.assertEqual(2, error.code)
-        self.assertEqual("badder juju", error.message)
-
-    def test_deep_size_non_nested(self):
-        test_hash = {
-            "errors": [
-                {"attribute": "one", "code": 1, "message": "is too long"},
-                {"attribute": "two", "code": 2, "message": "contains invalid chars"},
-                {"attribute": "thr", "code": 3, "message": "is invalid"}
-            ]
-        }
-
-        self.assertEqual(3, ValidationErrorCollection(test_hash).deep_size)
-
-    def test_deep_size_nested(self):
-        test_hash = {
-            "errors": [{"attribute": "one", "code": 1, "message": "is too long"}],
-            "nested": {
-                "errors": [{"attribute": "two", "code": 2, "message": "contains invalid chars"}]
-            }
-        }
-
-        self.assertEqual(2, ValidationErrorCollection(test_hash).deep_size)
-
-    def test_deep_size_multiple_nestings(self):
-        test_hash = {
-            "errors": [{"attribute": "one", "code": 1, "message": "is too long"}],
-            "nested": {
-                "errors": [{"attribute": "two", "code": 2, "message": "contains invalid chars"}],
-                "nested_again": {
-                    "errors": [
-                        {"attribute": "three", "code": 3, "message": "super nested"},
-                        {"attribute": "four", "code": 4, "message": "super nested 2"}
-                    ]
-                }
-            }
-        }
-
-        self.assertEqual(4, ValidationErrorCollection(test_hash).deep_size)
-
-    def test_len_multiple_nestings(self):
-        test_hash = {
-            "errors": [{"attribute": "one", "code": 1, "message": "is too long"}],
-            "nested": {
-                "errors": [{"attribute": "two", "code": 2, "message": "contains invalid chars"}],
-                "nested_again": {
-                    "errors": [
-                        {"attribute": "three", "code": 3, "message": "super nested"},
-                        {"attribute": "four", "code": 4, "message": "super nested 2"}
-                    ]
-                }
-            }
-        }
-        validation_error_collection = ValidationErrorCollection(test_hash)
-        self.assertEqual(1, len(validation_error_collection))
-        self.assertEqual(1, len(validation_error_collection.for_object("nested")))
-        self.assertEqual(2, len(validation_error_collection.for_object("nested").for_object("nested_again")))
-
-    def test_deep_errors(self):
-        test_hash = {
-            "errors": [{"attribute": "one", "code": 1, "message": "is too long"}],
-            "nested": {
-                "errors": [{"attribute": "two", "code": 2, "message": "contains invalid chars"}],
-                "nested_again": {
-                    "errors": [
-                        {"attribute": "three", "code": 3, "message": "super nested"},
-                        {"attribute": "four", "code": 4, "message": "super nested 2"}
-                    ]
-                }
-            }
-        }
-        validation_error_collection = ValidationErrorCollection(test_hash)
-        self.assertEqual([1, 2, 3, 4], [error.code for error in validation_error_collection.deep_errors])
-
-    def test_errors(self):
-        test_hash = {
-            "errors": [{"attribute": "one", "code": 1, "message": "is too long"}],
-            "nested": {
-                "errors": [{"attribute": "two", "code": 2, "message": "contains invalid chars"}],
-                "nested_again": {
-                    "errors": [
-                        {"attribute": "three", "code": 3, "message": "super nested"},
-                        {"attribute": "four", "code": 4, "message": "super nested 2"}
-                    ]
-                }
-            }
-        }
-        validation_error_collection = ValidationErrorCollection(test_hash)
-
-        self.assertEqual([1], [error.code for error in validation_error_collection.errors])
-
-        self.assertEqual([2], [error.code for error in validation_error_collection.for_object("nested").errors])
-        self.assertEqual([3, 4], [error.code for error in validation_error_collection.for_object("nested").for_object("nested_again").errors])
diff --git a/tests/unit/test_webhooks.py b/tests/unit/test_webhooks.py
deleted file mode 100644
index 943bec7..0000000
--- a/tests/unit/test_webhooks.py
+++ /dev/null
@@ -1,645 +0,0 @@
-from tests.test_helper import *
-from datetime import date
-from braintree.dispute import Dispute
-from braintree.credit_card import CreditCard
-from braintree.paypal_account import PayPalAccount
-from braintree.venmo_account import VenmoAccount
-
-class TestWebhooks(unittest.TestCase):
-    def test_sample_notification_builds_a_parsable_notification(self):
-        sample_notification = WebhookTesting.sample_notification(
-            WebhookNotification.Kind.SubscriptionWentPastDue,
-            "my_id"
-        )
-
-        notification = WebhookNotification.parse(sample_notification['bt_signature'], sample_notification['bt_payload'])
-
-        self.assertEqual(WebhookNotification.Kind.SubscriptionWentPastDue, notification.kind)
-        self.assertEqual("my_id", notification.subscription.id)
-        self.assertTrue((datetime.utcnow() - notification.timestamp).seconds < 10)
-        self.assertIsNone(notification.source_merchant_id)
-
-    def test_sample_notification_with_source_merchant_id(self):
-        sample_notification = WebhookTesting.sample_notification(
-            WebhookNotification.Kind.SubscriptionWentPastDue,
-            'my_id',
-            'my_source_merchant_id'
-        )
-
-        notification = WebhookNotification.parse(sample_notification['bt_signature'], sample_notification['bt_payload'])
-
-        self.assertEqual('my_source_merchant_id', notification.source_merchant_id)
-
-    @raises(InvalidSignatureError)
-    def test_completely_invalid_signature(self):
-        sample_notification = WebhookTesting.sample_notification(
-            WebhookNotification.Kind.SubscriptionWentPastDue,
-            "my_id"
-        )
-
-        WebhookNotification.parse("bad_stuff", sample_notification['bt_payload'])
-
-    def test_parse_raises_when_public_key_is_wrong(self):
-        sample_notification = WebhookTesting.sample_notification(
-            WebhookNotification.Kind.SubscriptionWentPastDue,
-            "my_id"
-        )
-
-        config = Configuration(
-            environment=Environment.Development,
-            merchant_id="integration_merchant_id",
-            public_key="wrong_public_key",
-            private_key="wrong_private_key"
-        )
-        gateway = BraintreeGateway(config)
-
-        try:
-            gateway.webhook_notification.parse(sample_notification['bt_signature'], sample_notification['bt_payload'])
-        except InvalidSignatureError as e:
-            self.assertEqual("no matching public key", str(e))
-        else:
-            self.assertFalse("raises exception")
-
-    def test_invalid_signature_when_payload_modified(self):
-        sample_notification = WebhookTesting.sample_notification(
-            WebhookNotification.Kind.SubscriptionWentPastDue,
-            "my_id"
-        )
-
-        try:
-            WebhookNotification.parse(sample_notification['bt_signature'], b"badstuff" + sample_notification['bt_payload'])
-        except InvalidSignatureError as e:
-            self.assertEqual("signature does not match payload - one has been modified", str(e))
-        else:
-            self.assertFalse("raises exception")
-
-    def test_parse_raise_exception_if_signature_is_blank(self):
-        try:
-            WebhookNotification.parse(None, "payload")
-        except InvalidSignatureError as e:
-            self.assertEqual("signature cannot be blank", str(e))
-        else:
-            self.assertFalse("raises exception")
-
-    def test_parse_raise_exception_if_payload_is_blank(self):
-        try:
-            WebhookNotification.parse("signature", None)
-        except InvalidSignatureError as e:
-            self.assertEqual("payload cannot be blank", str(e))
-        else:
-            self.assertFalse("raises exception")
-
-    def test_invalid_signature_when_bontains_invalid_characters(self):
-        sample_notification = WebhookTesting.sample_notification(
-            WebhookNotification.Kind.SubscriptionWentPastDue,
-            "my_id"
-        )
-
-        try:
-            WebhookNotification.parse(sample_notification['bt_signature'], "~* invalid! *~")
-        except InvalidSignatureError as e:
-            self.assertEqual("payload contains illegal characters", str(e))
-        else:
-            self.assertFalse("raises exception")
-
-    def test_parse_allows_all_valid_characters(self):
-        sample_notification = WebhookTesting.sample_notification(
-            WebhookNotification.Kind.SubscriptionWentPastDue,
-            "my_id"
-        )
-
-        try:
-            WebhookNotification.parse(sample_notification['bt_signature'], "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+=/\n")
-        except InvalidSignatureError as e:
-            self.assertNotEqual("payload contains illegal characters", str(e))
-
-    def test_parse_retries_payload_with_a_newline(self):
-        sample_notification = WebhookTesting.sample_notification(
-            WebhookNotification.Kind.SubscriptionWentPastDue,
-            "my_id"
-        )
-
-        notification = WebhookNotification.parse(sample_notification['bt_signature'], sample_notification['bt_payload'].rstrip())
-
-        self.assertEqual(WebhookNotification.Kind.SubscriptionWentPastDue, notification.kind)
-        self.assertEqual("my_id", notification.subscription.id)
-        self.assertTrue((datetime.utcnow() - notification.timestamp).seconds < 10)
-
-    def test_verify_returns_a_correct_challenge_response(self):
-        response = WebhookNotification.verify("20f9f8ed05f77439fe955c977e4c8a53")
-        self.assertEqual("integration_public_key|d9b899556c966b3f06945ec21311865d35df3ce4", response)
-
-    def test_verify_raises_when_challenge_is_invalid(self):
-        try:
-            WebhookNotification.verify("bad challenge")
-        except InvalidChallengeError as e:
-            self.assertEqual("challenge contains non-hex characters", str(e))
-        else:
-            self.assertFalse("raises exception")
-
-    def test_builds_notification_for_approved_sub_merchant_account(self):
-        sample_notification = WebhookTesting.sample_notification(
-            WebhookNotification.Kind.SubMerchantAccountApproved,
-            "my_id"
-        )
-
-        notification = WebhookNotification.parse(sample_notification['bt_signature'], sample_notification['bt_payload'])
-
-        self.assertEqual(WebhookNotification.Kind.SubMerchantAccountApproved, notification.kind)
-        self.assertEqual("my_id", notification.merchant_account.id)
-        self.assertEqual(MerchantAccount.Status.Active, notification.merchant_account.status)
-        self.assertEqual("master_ma_for_my_id", notification.merchant_account.master_merchant_account.id)
-        self.assertEqual(MerchantAccount.Status.Active, notification.merchant_account.master_merchant_account.status)
-
-    def test_builds_notification_for_declined_sub_merchant_account(self):
-        sample_notification = WebhookTesting.sample_notification(
-            WebhookNotification.Kind.SubMerchantAccountDeclined,
-            "my_id"
-        )
-
-        notification = WebhookNotification.parse(sample_notification['bt_signature'], sample_notification['bt_payload'])
-
-        self.assertEqual(WebhookNotification.Kind.SubMerchantAccountDeclined, notification.kind)
-        self.assertEqual("my_id", notification.merchant_account.id)
-        self.assertEqual(MerchantAccount.Status.Suspended, notification.merchant_account.status)
-        self.assertEqual("master_ma_for_my_id", notification.merchant_account.master_merchant_account.id)
-        self.assertEqual(MerchantAccount.Status.Suspended, notification.merchant_account.master_merchant_account.status)
-        self.assertEqual("Credit score is too low", notification.message)
-        self.assertEqual(ErrorCodes.MerchantAccount.DeclinedOFAC, notification.errors.for_object("merchant_account").on("base")[0].code)
-
-    def test_builds_notification_for_disbursed_transactions(self):
-        sample_notification = WebhookTesting.sample_notification(
-            WebhookNotification.Kind.TransactionDisbursed,
-            "my_id"
-        )
-
-        notification = WebhookNotification.parse(sample_notification['bt_signature'], sample_notification['bt_payload'])
-
-        self.assertEqual(WebhookNotification.Kind.TransactionDisbursed, notification.kind)
-        self.assertEqual("my_id", notification.transaction.id)
-        self.assertEqual(100, notification.transaction.amount)
-        self.assertEqual(datetime(2013, 7, 9, 18, 23, 29), notification.transaction.disbursement_details.disbursement_date)
-
-    def test_builds_notification_for_settled_transactions(self):
-        sample_notification = WebhookTesting.sample_notification(
-            WebhookNotification.Kind.TransactionSettled,
-            "my_id"
-        )
-
-        notification = WebhookNotification.parse(sample_notification['bt_signature'], sample_notification['bt_payload'])
-
-        self.assertEqual(WebhookNotification.Kind.TransactionSettled, notification.kind)
-        self.assertEqual("my_id", notification.transaction.id)
-        self.assertEqual("settled", notification.transaction.status)
-        self.assertEqual(100, notification.transaction.amount)
-        self.assertEqual(notification.transaction.us_bank_account.routing_number, "123456789")
-        self.assertEqual(notification.transaction.us_bank_account.last_4, "1234")
-        self.assertEqual(notification.transaction.us_bank_account.account_type, "checking")
-        self.assertEqual(notification.transaction.us_bank_account.account_holder_name, "Dan Schulman")
-
-    def test_builds_notification_for_settlement_declined_transactions(self):
-        sample_notification = WebhookTesting.sample_notification(
-            WebhookNotification.Kind.TransactionSettlementDeclined,
-            "my_id"
-        )
-
-        notification = WebhookNotification.parse(sample_notification['bt_signature'], sample_notification['bt_payload'])
-
-        self.assertEqual(WebhookNotification.Kind.TransactionSettlementDeclined, notification.kind)
-        self.assertEqual("my_id", notification.transaction.id)
-        self.assertEqual("settlement_declined", notification.transaction.status)
-        self.assertEqual(100, notification.transaction.amount)
-        self.assertEqual(notification.transaction.us_bank_account.routing_number, "123456789")
-        self.assertEqual(notification.transaction.us_bank_account.last_4, "1234")
-        self.assertEqual(notification.transaction.us_bank_account.account_type, "checking")
-        self.assertEqual(notification.transaction.us_bank_account.account_holder_name, "Dan Schulman")
-
-    def test_builds_notification_for_disbursements(self):
-        sample_notification = WebhookTesting.sample_notification(
-            WebhookNotification.Kind.Disbursement,
-            "my_id"
-        )
-
-        notification = WebhookNotification.parse(sample_notification['bt_signature'], sample_notification['bt_payload'])
-
-        self.assertEqual(WebhookNotification.Kind.Disbursement, notification.kind)
-        self.assertEqual("my_id", notification.disbursement.id)
-        self.assertEqual(100, notification.disbursement.amount)
-        self.assertEqual(None, notification.disbursement.exception_message)
-        self.assertEqual(None, notification.disbursement.follow_up_action)
-        self.assertEqual(date(2014, 2, 9), notification.disbursement.disbursement_date)
-
-    def test_builds_notification_for_disbursement_exceptions(self):
-        sample_notification = WebhookTesting.sample_notification(
-            WebhookNotification.Kind.DisbursementException,
-            "my_id"
-        )
-
-        notification = WebhookNotification.parse(sample_notification['bt_signature'], sample_notification['bt_payload'])
-
-        self.assertEqual(WebhookNotification.Kind.DisbursementException, notification.kind)
-        self.assertEqual("my_id", notification.disbursement.id)
-        self.assertEqual(100, notification.disbursement.amount)
-        self.assertEqual("bank_rejected", notification.disbursement.exception_message)
-        self.assertEqual("update_funding_information", notification.disbursement.follow_up_action)
-        self.assertEqual(date(2014, 2, 9), notification.disbursement.disbursement_date)
-
-    def test_builds_notification_for_old_dispute_opened(self):
-        sample_notification = WebhookTesting.sample_notification(
-            WebhookNotification.Kind.DisputeOpened,
-            "legacy_dispute_id"
-        )
-
-        notification = WebhookNotification.parse(sample_notification['bt_signature'], sample_notification['bt_payload'])
-
-        self.assertEqual(WebhookNotification.Kind.DisputeOpened, notification.kind)
-        self.assertEqual("legacy_dispute_id", notification.dispute.id)
-        self.assertEqual(Dispute.Status.Open, notification.dispute.status)
-        self.assertEqual(Dispute.Kind.Chargeback, notification.dispute.kind)
-        self.assertEqual(notification.dispute.date_opened, date(2014, 3, 28))
-
-    def test_builds_notification_for_old_dispute_lost(self):
-        sample_notification = WebhookTesting.sample_notification(
-            WebhookNotification.Kind.DisputeLost,
-            "legacy_dispute_id"
-        )
-
-        notification = WebhookNotification.parse(sample_notification['bt_signature'], sample_notification['bt_payload'])
-
-        self.assertEqual(WebhookNotification.Kind.DisputeLost, notification.kind)
-        self.assertEqual("legacy_dispute_id", notification.dispute.id)
-        self.assertEqual(Dispute.Status.Lost, notification.dispute.status)
-        self.assertEqual(Dispute.Kind.Chargeback, notification.dispute.kind)
-        self.assertEqual(notification.dispute.date_opened, date(2014, 3, 28))
-
-    def test_builds_notification_for_old_dispute_won(self):
-        sample_notification = WebhookTesting.sample_notification(
-            WebhookNotification.Kind.DisputeWon,
-            "legacy_dispute_id"
-        )
-
-        notification = WebhookNotification.parse(sample_notification['bt_signature'], sample_notification['bt_payload'])
-
-        self.assertEqual(WebhookNotification.Kind.DisputeWon, notification.kind)
-        self.assertEqual("legacy_dispute_id", notification.dispute.id)
-        self.assertEqual(Dispute.Status.Won, notification.dispute.status)
-        self.assertEqual(Dispute.Kind.Chargeback, notification.dispute.kind)
-        self.assertEqual(notification.dispute.date_opened, date(2014, 3, 28))
-        self.assertEqual(notification.dispute.date_won, date(2014, 9, 1))
-
-    def test_builds_notification_for_new_dispute_opened(self):
-        sample_notification = WebhookTesting.sample_notification(
-            WebhookNotification.Kind.DisputeOpened,
-            "my_id"
-        )
-
-        notification = WebhookNotification.parse(sample_notification['bt_signature'], sample_notification['bt_payload'])
-
-        self.assertEqual(WebhookNotification.Kind.DisputeOpened, notification.kind)
-        self.assertEqual("my_id", notification.dispute.id)
-        self.assertEqual(Dispute.Status.Open, notification.dispute.status)
-        self.assertEqual(Dispute.Kind.Chargeback, notification.dispute.kind)
-        self.assertEqual(notification.dispute.date_opened, date(2014, 3, 28))
-
-    def test_builds_notification_for_new_dispute_lost(self):
-        sample_notification = WebhookTesting.sample_notification(
-            WebhookNotification.Kind.DisputeLost,
-            "my_id"
-        )
-
-        notification = WebhookNotification.parse(sample_notification['bt_signature'], sample_notification['bt_payload'])
-
-        self.assertEqual(WebhookNotification.Kind.DisputeLost, notification.kind)
-        self.assertEqual("my_id", notification.dispute.id)
-        self.assertEqual(Dispute.Status.Lost, notification.dispute.status)
-        self.assertEqual(Dispute.Kind.Chargeback, notification.dispute.kind)
-        self.assertEqual(notification.dispute.date_opened, date(2014, 3, 28))
-
-    def test_builds_notification_for_new_dispute_won(self):
-        sample_notification = WebhookTesting.sample_notification(
-            WebhookNotification.Kind.DisputeWon,
-            "my_id"
-        )
-
-        notification = WebhookNotification.parse(sample_notification['bt_signature'], sample_notification['bt_payload'])
-
-        self.assertEqual(WebhookNotification.Kind.DisputeWon, notification.kind)
-        self.assertEqual("my_id", notification.dispute.id)
-        self.assertEqual(Dispute.Status.Won, notification.dispute.status)
-        self.assertEqual(Dispute.Kind.Chargeback, notification.dispute.kind)
-        self.assertEqual(notification.dispute.date_opened, date(2014, 3, 28))
-        self.assertEqual(notification.dispute.date_won, date(2014, 9, 1))
-
-    def test_builds_notification_for_partner_merchant_connected(self):
-        sample_notification = WebhookTesting.sample_notification(
-            WebhookNotification.Kind.PartnerMerchantConnected,
-            "my_id"
-        )
-
-        notification = WebhookNotification.parse(sample_notification['bt_signature'], sample_notification['bt_payload'])
-
-        self.assertEqual(WebhookNotification.Kind.PartnerMerchantConnected, notification.kind)
-        self.assertEqual("abc123", notification.partner_merchant.partner_merchant_id)
-        self.assertEqual("public_key", notification.partner_merchant.public_key)
-        self.assertEqual("private_key", notification.partner_merchant.private_key)
-        self.assertEqual("public_id", notification.partner_merchant.merchant_public_id)
-        self.assertEqual("cse_key", notification.partner_merchant.client_side_encryption_key)
-        self.assertTrue((datetime.utcnow() - notification.timestamp).seconds < 10)
-
-    def test_builds_notification_for_partner_merchant_disconnected(self):
-        sample_notification = WebhookTesting.sample_notification(
-            WebhookNotification.Kind.PartnerMerchantDisconnected,
-            "my_id"
-        )
-
-        notification = WebhookNotification.parse(sample_notification['bt_signature'], sample_notification['bt_payload'])
-
-        self.assertEqual(WebhookNotification.Kind.PartnerMerchantDisconnected, notification.kind)
-        self.assertEqual("abc123", notification.partner_merchant.partner_merchant_id)
-        self.assertTrue((datetime.utcnow() - notification.timestamp).seconds < 10)
-
-    def test_builds_notification_for_partner_merchant_declined(self):
-        sample_notification = WebhookTesting.sample_notification(
-            WebhookNotification.Kind.PartnerMerchantDeclined,
-            "my_id"
-        )
-
-        notification = WebhookNotification.parse(sample_notification['bt_signature'], sample_notification['bt_payload'])
-
-        self.assertEqual(WebhookNotification.Kind.PartnerMerchantDeclined, notification.kind)
-        self.assertEqual("abc123", notification.partner_merchant.partner_merchant_id)
-        self.assertTrue((datetime.utcnow() - notification.timestamp).seconds < 10)
-
-    def test_builds_notification_for_oauth_access_revoked(self):
-        sample_notification = WebhookTesting.sample_notification(
-            WebhookNotification.Kind.OAuthAccessRevoked,
-            "my_id"
-        )
-
-        notification = WebhookNotification.parse(sample_notification["bt_signature"], sample_notification["bt_payload"])
-
-        self.assertEqual(WebhookNotification.Kind.OAuthAccessRevoked, notification.kind)
-        self.assertEqual("my_id", notification.oauth_access_revocation.merchant_id)
-        self.assertEqual("oauth_application_client_id", notification.oauth_access_revocation.oauth_application_client_id)
-        self.assertTrue((datetime.utcnow() - notification.timestamp).seconds < 10)
-
-    def test_builds_notification_for_connected_merchant_status_transitioned(self):
-        sample_notification = WebhookTesting.sample_notification(
-            WebhookNotification.Kind.ConnectedMerchantStatusTransitioned,
-            "my_id"
-        )
-
-        notification = WebhookNotification.parse(sample_notification['bt_signature'], sample_notification['bt_payload'])
-
-        self.assertEqual(WebhookNotification.Kind.ConnectedMerchantStatusTransitioned, notification.kind)
-        self.assertEqual("new_status", notification.connected_merchant_status_transitioned.status)
-        self.assertEqual("my_id", notification.connected_merchant_status_transitioned.merchant_public_id)
-        self.assertEqual("my_id", notification.connected_merchant_status_transitioned.merchant_id)
-        self.assertEqual("oauth_application_client_id", notification.connected_merchant_status_transitioned.oauth_application_client_id)
-
-    def test_builds_notification_for_connected_merchant_paypal_status_changed(self):
-        sample_notification = WebhookTesting.sample_notification(
-            WebhookNotification.Kind.ConnectedMerchantPayPalStatusChanged,
-            "my_id"
-        )
-
-        notification = WebhookNotification.parse(sample_notification['bt_signature'], sample_notification['bt_payload'])
-
-        self.assertEqual(WebhookNotification.Kind.ConnectedMerchantPayPalStatusChanged, notification.kind)
-        self.assertEqual("link", notification.connected_merchant_paypal_status_changed.action)
-        self.assertEqual("my_id", notification.connected_merchant_paypal_status_changed.merchant_public_id)
-        self.assertEqual("my_id", notification.connected_merchant_paypal_status_changed.merchant_id)
-        self.assertEqual("oauth_application_client_id", notification.connected_merchant_paypal_status_changed.oauth_application_client_id)
-
-    def test_builds_notification_for_subscription_charged_successfully(self):
-        sample_notification = WebhookTesting.sample_notification(
-            WebhookNotification.Kind.SubscriptionChargedSuccessfully,
-            "my_id"
-        )
-
-        notification = WebhookNotification.parse(sample_notification['bt_signature'], sample_notification['bt_payload'])
-
-        self.assertEqual(WebhookNotification.Kind.SubscriptionChargedSuccessfully, notification.kind)
-        self.assertEqual("my_id", notification.subscription.id)
-        self.assertTrue(len(notification.subscription.transactions) == 1)
-
-        transaction = notification.subscription.transactions.pop()
-
-        self.assertEqual("submitted_for_settlement", transaction.status)
-        self.assertEqual(Decimal("49.99"), transaction.amount)
-
-    def test_builds_notification_for_subscription_charged_unsuccessfully(self):
-        sample_notification = WebhookTesting.sample_notification(
-            WebhookNotification.Kind.SubscriptionChargedUnsuccessfully,
-            "my_id"
-        )
-
-        notification = WebhookNotification.parse(sample_notification['bt_signature'], sample_notification['bt_payload'])
-
-        self.assertEqual(WebhookNotification.Kind.SubscriptionChargedUnsuccessfully, notification.kind)
-        self.assertEqual("my_id", notification.subscription.id)
-        self.assertTrue(len(notification.subscription.transactions) == 1)
-
-        transaction = notification.subscription.transactions.pop()
-
-        self.assertEqual("failed", transaction.status)
-        self.assertEqual(Decimal("49.99"), transaction.amount)
-
-    def test_builds_notification_for_check(self):
-        sample_notification = WebhookTesting.sample_notification(
-            WebhookNotification.Kind.Check,
-            ""
-        )
-
-        notification = WebhookNotification.parse(sample_notification['bt_signature'], sample_notification['bt_payload'])
-
-        self.assertEqual(WebhookNotification.Kind.Check, notification.kind)
-
-    def test_builds_notification_for_account_updater_daily_report_webhook(self):
-        sample_notification = WebhookTesting.sample_notification(
-            WebhookNotification.Kind.AccountUpdaterDailyReport,
-            "my_id"
-        )
-
-        notification = WebhookNotification.parse(sample_notification['bt_signature'], sample_notification['bt_payload'])
-
-        self.assertEqual(WebhookNotification.Kind.AccountUpdaterDailyReport, notification.kind)
-        self.assertEqual("link-to-csv-report", notification.account_updater_daily_report.report_url)
-        self.assertEqual(date(2016, 1, 14), notification.account_updater_daily_report.report_date)
-
-    def test_grantor_updated_granted_payment_method_webhook(self):
-        sample_notification = WebhookTesting.sample_notification(
-            WebhookNotification.Kind.GrantorUpdatedGrantedPaymentMethod,
-            "my_id"
-        )
-
-        notification = WebhookNotification.parse(sample_notification["bt_signature"], sample_notification["bt_payload"])
-        update = notification.granted_payment_instrument_update
-
-        self.assertEqual(WebhookNotification.Kind.GrantorUpdatedGrantedPaymentMethod, notification.kind)
-        self.assertEqual("vczo7jqrpwrsi2px", update.grant_owner_merchant_id)
-        self.assertEqual("cf0i8wgarszuy6hc", update.grant_recipient_merchant_id)
-        self.assertEqual("ee257d98-de40-47e8-96b3-a6954ea7a9a4", update.payment_method_nonce)
-        self.assertEqual("abc123z", update.token)
-        self.assertEqual(["expiration-month", "expiration-year"], update.updated_fields)
-
-    def test_recipient_updated_granted_payment_method_webhook(self):
-        sample_notification = WebhookTesting.sample_notification(
-            WebhookNotification.Kind.RecipientUpdatedGrantedPaymentMethod,
-            "my_id"
-        )
-
-        notification = WebhookNotification.parse(sample_notification["bt_signature"], sample_notification["bt_payload"])
-        update = notification.granted_payment_instrument_update
-
-        self.assertEqual(WebhookNotification.Kind.RecipientUpdatedGrantedPaymentMethod, notification.kind)
-        self.assertEqual("vczo7jqrpwrsi2px", update.grant_owner_merchant_id)
-        self.assertEqual("cf0i8wgarszuy6hc", update.grant_recipient_merchant_id)
-        self.assertEqual("ee257d98-de40-47e8-96b3-a6954ea7a9a4", update.payment_method_nonce)
-        self.assertEqual("abc123z", update.token)
-        self.assertEqual(["expiration-month", "expiration-year"], update.updated_fields)
-
-    def test_granted_payment_method_revoked_credit_card_webhook(self):
-        xml_payload = """
-            <notification>
-                <source-merchant-id>12345</source-merchant-id>
-                <timestamp type="datetime">2018-10-10T22:46:41Z</timestamp>
-                <kind>granted_payment_method_revoked</kind>
-                <subject>
-                    <credit-card>
-                        <bin>555555</bin>
-                        <card-type>MasterCard</card-type>
-                        <cardholder-name>Amber Ankunding</cardholder-name>
-                        <commercial>Unknown</commercial>
-                        <country-of-issuance>Unknown</country-of-issuance>
-                        <created-at type="datetime">2018-10-10T22:46:41Z</created-at>
-                        <customer-id>credit_card_customer_id</customer-id>
-                        <customer-location>US</customer-location>
-                        <debit>Unknown</debit>
-                        <default type="boolean">true</default>
-                        <durbin-regulated>Unknown</durbin-regulated>
-                        <expiration-month>06</expiration-month>
-                        <expiration-year>2020</expiration-year>
-                        <expired type="boolean">false</expired>
-                        <global-id>cGF5bWVudG1ldGhvZF8zcHQ2d2hz</global-id>
-                        <healthcare>Unknown</healthcare>
-                        <image-url>https://assets.braintreegateway.com/payment_method_logo/mastercard.png?environment=test</image-url>
-                        <issuing-bank>Unknown</issuing-bank>
-                        <last-4>4444</last-4>
-                        <payroll>Unknown</payroll>
-                        <prepaid>Unknown</prepaid>
-                        <product-id>Unknown</product-id>
-                        <subscriptions type="array"/>
-                        <token>credit_card_token</token>
-                        <unique-number-identifier>08199d188e37460163207f714faf074a</unique-number-identifier>
-                        <updated-at type="datetime">2018-10-10T22:46:41Z</updated-at>
-                        <venmo-sdk type="boolean">false</venmo-sdk>
-                        <verifications type="array"/>
-                    </credit-card>
-                </subject>
-            </notification>
-            """.encode("utf-8")
-        sample_notification = TestHelper.sample_notification_from_xml(xml_payload)
-
-        notification = WebhookNotification.parse(sample_notification["bt_signature"], sample_notification["bt_payload"])
-        metadata = notification.revoked_payment_method_metadata
-
-        self.assertEqual(WebhookNotification.Kind.GrantedPaymentMethodRevoked, notification.kind)
-        self.assertEqual("credit_card_customer_id", metadata.customer_id)
-        self.assertEqual("credit_card_token", metadata.token)
-        self.assertTrue(isinstance(metadata.revoked_payment_method, CreditCard))
-
-    def test_granted_payment_method_revoked_paypal_account_webhook(self):
-        xml_payload = """
-            <notification>
-                <source-merchant-id>12345</source-merchant-id>
-                <timestamp type="datetime">2018-10-10T22:46:41Z</timestamp>
-                <kind>granted_payment_method_revoked</kind>
-                <subject>
-                    <paypal-account>
-                        <billing-agreement-id>billing_agreement_id</billing-agreement-id>
-                        <created-at type="dateTime">2018-10-11T21:10:33Z</created-at>
-                        <customer-id>paypal_customer_id</customer-id>
-                        <default type="boolean">true</default>
-                        <email>johndoe@example.com</email>
-                        <global-id>cGF5bWVudG1ldGhvZF9wYXlwYWxfdG9rZW4</global-id>
-                        <image-url>https://assets.braintreegateway.com/payment_method_logo/mastercard.png?environment=test</image-url>
-                        <subscriptions type="array"></subscriptions>
-                        <token>paypal_token</token>
-                        <updated-at type="dateTime">2018-10-11T21:10:33Z</updated-at>
-                        <payer-id>a6a8e1a4</payer-id>
-                    </paypal-account>
-                </subject>
-            </notification>
-            """.encode("utf-8")
-        sample_notification = TestHelper.sample_notification_from_xml(xml_payload)
-        notification = WebhookNotification.parse(sample_notification["bt_signature"], sample_notification["bt_payload"])
-        metadata = notification.revoked_payment_method_metadata
-
-        self.assertEqual(WebhookNotification.Kind.GrantedPaymentMethodRevoked, notification.kind)
-        self.assertEqual("paypal_customer_id", metadata.customer_id)
-        self.assertEqual("paypal_token", metadata.token)
-        self.assertTrue(isinstance(metadata.revoked_payment_method, PayPalAccount))
-
-    def test_granted_payment_method_revoked_venmo_account_webhook(self):
-        xml_payload = """
-            <notification>
-                <source-merchant-id>12345</source-merchant-id>
-                <timestamp type="datetime">2018-10-10T22:46:41Z</timestamp>
-                <kind>granted_payment_method_revoked</kind>
-                <subject>
-                    <venmo-account>
-                        <created-at type="dateTime">2018-10-11T21:28:37Z</created-at>
-                        <updated-at type="dateTime">2018-10-11T21:28:37Z</updated-at>
-                        <default type="boolean">true</default>
-                        <image-url>https://assets.braintreegateway.com/payment_method_logo/mastercard.png?environment=test</image-url>
-                        <token>venmo_token</token>
-                        <source-description>Venmo Account: venmojoe</source-description>
-                        <username>venmojoe</username>
-                        <venmo-user-id>456</venmo-user-id>
-                        <subscriptions type="array"/>
-                        <customer-id>venmo_customer_id</customer-id>
-                        <global-id>cGF5bWVudG1ldGhvZF92ZW5tb2FjY291bnQ</global-id>
-                    </venmo-account>
-                </subject>
-            </notification>
-            """.encode("utf-8")
-        sample_notification = TestHelper.sample_notification_from_xml(xml_payload)
-
-        notification = WebhookNotification.parse(sample_notification["bt_signature"], sample_notification["bt_payload"])
-        metadata = notification.revoked_payment_method_metadata
-
-        self.assertEqual(WebhookNotification.Kind.GrantedPaymentMethodRevoked, notification.kind)
-        self.assertEqual("venmo_customer_id", metadata.customer_id)
-        self.assertEqual("venmo_token", metadata.token)
-        self.assertTrue(isinstance(metadata.revoked_payment_method, VenmoAccount))
-
-    def test_payment_method_revoked_by_customer_webhook(self):
-        sample_notification = WebhookTesting.sample_notification(
-            WebhookNotification.Kind.PaymentMethodRevokedByCustomer,
-            "my_payment_method_token"
-        )
-
-        notification = WebhookNotification.parse(sample_notification["bt_signature"], sample_notification["bt_payload"])
-        metadata = notification.revoked_payment_method_metadata
-
-        self.assertEqual(WebhookNotification.Kind.PaymentMethodRevokedByCustomer, notification.kind)
-        self.assertEqual("my_payment_method_token", metadata.token)
-        self.assertTrue(isinstance(metadata.revoked_payment_method, PayPalAccount))
-        self.assertNotEqual(None, metadata.revoked_payment_method.revoked_at)
-
-    def test_local_payment_completed_webhook(self):
-        sample_notification = WebhookTesting.sample_notification(
-            WebhookNotification.Kind.LocalPaymentCompleted,
-            "my_id"
-        )
-
-        notification = WebhookNotification.parse(sample_notification["bt_signature"], sample_notification["bt_payload"])
-        local_payment_completed = notification.local_payment_completed
-
-        self.assertEqual(WebhookNotification.Kind.LocalPaymentCompleted, notification.kind)
-        self.assertEqual("a-payment-id", local_payment_completed.payment_id)
-        self.assertEqual("a-payer-id", local_payment_completed.payer_id)
-        self.assertEqual("ee257d98-de40-47e8-96b3-a6954ea7a9a4", local_payment_completed.payment_method_nonce)
-        self.assertTrue(isinstance(local_payment_completed.transaction, Transaction))
-
diff --git a/tests/unit/test_xml_util.py b/tests/unit/test_xml_util.py
deleted file mode 100644
index ea926f3..0000000
--- a/tests/unit/test_xml_util.py
+++ /dev/null
@@ -1,167 +0,0 @@
-from tests.test_helper import *
-
-class TestXmlUtil(unittest.TestCase):
-    def test_dict_from_xml_simple(self):
-        xml = """
-        <container>val</container>
-        """
-        expected = {"container": "val"}
-        self.assertEqual(expected, XmlUtil.dict_from_xml(xml))
-
-    def test_dict_from_xml_typecasts_ints(self):
-        xml = """
-        <container type="integer">1</container>
-        """
-        expected = {"container": 1}
-        self.assertEqual(expected, XmlUtil.dict_from_xml(xml))
-
-    def test_dict_from_xml_typecasts_nils(self):
-        xml = """
-        <root>
-          <a_nil_value nil="true"></a_nil_value>
-          <an_empty_string></an_empty_string>
-        </root>
-        """
-        expected = {"root": {"a_nil_value": None, "an_empty_string": ""}}
-        self.assertEqual(expected, XmlUtil.dict_from_xml(xml))
-
-    def test_dict_from_xml_typecasts_booleans(self):
-        xml = """
-        <root>
-          <casted-true type="boolean">true</casted-true>
-          <casted-one type="boolean">1</casted-one>
-          <casted-false type="boolean">false</casted-false>
-          <casted-anything type="boolean">anything</casted-anything>
-          <uncasted-true>true</uncasted-true>
-        </root>
-        """
-        expected = {
-            "root": {
-                "casted_true": True,
-                "casted_one": True,
-                "casted_false": False,
-                "casted_anything": False,
-                "uncasted_true": "true"
-            }
-        }
-        self.assertEqual(expected, XmlUtil.dict_from_xml(xml))
-
-    def test_dict_from_xml_typecasts_datetimes(self):
-        xml = """
-        <root>
-          <created-at type="datetime">2009-10-28T10:19:49Z</created-at>
-        </root>
-        """
-        expected = {"root": {"created_at": datetime(2009, 10, 28, 10, 19, 49)}}
-        self.assertEqual(expected, XmlUtil.dict_from_xml(xml))
-
-    def test_dict_from_xml_with_dashes(self):
-        xml = """
-        <my-item>val</my-item>
-        """
-        expected = {"my_item": "val"}
-        self.assertEqual(expected, XmlUtil.dict_from_xml(xml))
-
-    def test_dict_from_xml_nested(self):
-        xml = """
-        <container>
-            <elem>val</elem>
-        </container>
-        """
-        expected = {"container": {"elem": "val"}}
-        self.assertEqual(expected, XmlUtil.dict_from_xml(xml))
-
-    def test_dict_from_xml_array(self):
-        xml = """
-        <container>
-            <elements type="array">
-                <elem>val1</elem>
-                <elem>val2</elem>
-                <elem>val3</elem>
-            </elements>
-        </container>
-        """
-        expected = {"container": {"elements": ["val1", "val2", "val3"]}}
-        self.assertEqual(expected, XmlUtil.dict_from_xml(xml))
-
-    def test_dict_from_xml_with_empty_array(self):
-        xml = """
-        <container>
-            <elements type="array" />
-        </container>
-        """
-        expected = {"container": {"elements": []}}
-        self.assertEqual(expected, XmlUtil.dict_from_xml(xml))
-
-    def test_dict_from_xml_array_of_hashes(self):
-        xml = """
-        <container>
-            <elements type="array">
-                <elem><val>val1</val></elem>
-                <elem><val>val2</val></elem>
-                <elem><val>val3</val></elem>
-            </elements>
-        </container>
-        """
-        expected = {"container": {"elements": [{"val": "val1"}, {"val": "val2"}, {"val": "val3"}]}}
-        self.assertEqual(expected, XmlUtil.dict_from_xml(xml))
-
-    def test_xml_from_dict_escapes_keys_and_values(self):
-        test_dict = {"k<ey": "va&lue"}
-        self.assertEqual("<k&lt;ey>va&amp;lue</k&lt;ey>", XmlUtil.xml_from_dict(test_dict))
-
-    def test_xml_from_dict_simple(self):
-        test_dict = {"a": "b"}
-        self.assertEqual(test_dict, self.__xml_and_back(test_dict))
-
-    def test_xml_from_dict_with_integer(self):
-        test_dict = {"a": 1}
-        self.assertEqual('<a type="integer">1</a>', XmlUtil.xml_from_dict(test_dict))
-
-    def test_xml_from_dict_with_long(self):
-        test_dict = {"a": 12341234123412341234}
-        self.assertEqual('<a type="integer">12341234123412341234</a>', XmlUtil.xml_from_dict(test_dict))
-
-    def test_xml_from_dict_with_boolean(self):
-        test_dict = {"a": True}
-        self.assertEqual(test_dict, self.__xml_and_back(test_dict))
-
-    def test_xml_from_dict_simple_xml_and_back_twice(self):
-        test_dict = {"a": "b"}
-        self.assertEqual(test_dict, self.__xml_and_back(self.__xml_and_back(test_dict)))
-
-    def test_xml_from_dict_nested(self):
-        test_dict = {"container": {"item": "val"}}
-        self.assertEqual(test_dict, self.__xml_and_back(test_dict))
-
-    def test_xml_from_dict_with_array(self):
-        test_dict = {"container": {"elements": ["val1", "val2", "val3"]}}
-        self.assertEqual(test_dict, self.__xml_and_back(test_dict))
-
-    def test_xml_from_dict_with_array_of_hashes(self):
-        test_dict = {"container": {"elements": [{"val": "val1"}, {"val": "val2"}, {"val": "val3"}]}}
-        self.assertEqual(test_dict, self.__xml_and_back(test_dict))
-
-    def test_xml_from_dict_retains_underscores(self):
-        test_dict = {"container": {"my_element": "val"}}
-        self.assertEqual(test_dict, self.__xml_and_back(test_dict))
-
-    def test_xml_from_dict_escapes_special_chars(self):
-        test_dict = {"container": {"element": "<&>'\""}}
-        self.assertEqual(test_dict, self.__xml_and_back(test_dict))
-
-    def test_xml_from_dict_with_datetime(self):
-        test_dict = {"a": datetime(2010, 1, 2, 3, 4, 5)}
-        self.assertEqual(test_dict, self.__xml_and_back(test_dict))
-
-    def test_xml_from_dict_with_unicode_characters(self):
-        test_dict = {"a": u"\u1f61hat?"}
-        self.assertEqual('<a>&#8033;hat?</a>', XmlUtil.xml_from_dict(test_dict))
-
-    def test_xml_from_dict_with_dates_formats_as_datetime(self):
-        test_dict = {"a": date(2010, 1, 2)}
-        self.assertEqual('<a type="datetime">2010-01-02T00:00:00Z</a>', XmlUtil.xml_from_dict(test_dict))
-
-    @staticmethod
-    def __xml_and_back(test_dict):
-        return XmlUtil.dict_from_xml(XmlUtil.xml_from_dict(test_dict))
diff --git a/tests/unit/util/__init__.py b/tests/unit/util/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/tests/unit/util/test_constants.py b/tests/unit/util/test_constants.py
deleted file mode 100644
index bc07fff..0000000
--- a/tests/unit/util/test_constants.py
+++ /dev/null
@@ -1,5 +0,0 @@
-from tests.test_helper import *
-
-class TestConstants(unittest.TestCase):
-    def test_get_all_constant_values_from_class(self):
-        self.assertEqual(["Active", "Canceled", "Expired", "Past Due", "Pending"], Constants.get_all_constant_values_from_class(Subscription.Status))
diff --git a/tests/unit/util/test_datetime_parser.py b/tests/unit/util/test_datetime_parser.py
deleted file mode 100644
index 81e698e..0000000
--- a/tests/unit/util/test_datetime_parser.py
+++ /dev/null
@@ -1,31 +0,0 @@
-import unittest
-from braintree.util.datetime_parser import parse_datetime as parse
-from datetime import datetime
-
-
-class TestDateParser(unittest.TestCase):
-    def test_parses_with_zulu_and_symbols(self):
-        self.assertEqual(parse('2017-04-19T18:51:21Z'), datetime(2017, 4, 19, 18, 51, 21))
-        self.assertEqual(parse('2017-04-19T18:51:21.45Z'), datetime(2017, 4, 19, 18, 51, 21, 450000))
-
-    def test_parses_with_zulu_and_no_symbols(self):
-        self.assertEqual(parse('20170419T185121Z'), datetime(2017, 4, 19, 18, 51, 21))
-        self.assertEqual(parse('20170419T185121.123Z'), datetime(2017, 4, 19, 18, 51, 21, 123000))
-
-    def test_parses_with_zero_offset(self):
-        self.assertEqual(parse('2017-04-19T18:51:21+00:00'), datetime(2017, 4, 19, 18, 51, 21))
-        self.assertEqual(parse('2017-04-19T18:51:21.420+00:00'), datetime(2017, 4, 19, 18, 51, 21, 420000))
-
-    def test_parses_with_negative_offset(self):
-        self.assertEqual(parse('2017-04-19T18:51:21-01:30'), datetime(2017, 4, 19, 20, 21, 21))
-        self.assertEqual(parse('2017-04-19T18:51:21.987-01:30'), datetime(2017, 4, 19, 20, 21, 21, 987000))
-
-    def test_parses_with_positive_offset(self):
-        self.assertEqual(parse('2017-04-19T18:51:21+07:00'), datetime(2017, 4, 19, 11, 51, 21))
-        self.assertEqual(parse('2017-04-19T18:51:21.765+07:00'), datetime(2017, 4, 19, 11, 51, 21, 765000))
-
-    def test_raises_with_bad_input(self):
-        with self.assertRaises(ValueError):
-            parse('20170420')
-        with self.assertRaises(ValueError):
-            parse('20170420Z')

Debdiff

[The following lists of changes regard files as different if they have different names, permissions or owners.]

Files in second set of .debs but not in first

-rw-r--r--  root/root   /usr/lib/python3/dist-packages/braintree-4.18.0.egg-info/PKG-INFO
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/braintree-4.18.0.egg-info/dependency_links.txt
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/braintree-4.18.0.egg-info/not-zip-safe
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/braintree-4.18.0.egg-info/requires.txt
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/braintree-4.18.0.egg-info/top_level.txt
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/braintree/apple_pay_gateway.py
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/braintree/apple_pay_options.py
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/braintree/dispute_details/paypal_message.py
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/braintree/enriched_customer_data.py
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/braintree/exceptions/gateway_timeout_error.py
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/braintree/exceptions/request_timeout_error.py
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/braintree/exceptions/service_unavailable_error.py
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/braintree/exchange_rate_quote.py
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/braintree/exchange_rate_quote_gateway.py
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/braintree/exchange_rate_quote_input.py
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/braintree/exchange_rate_quote_payload.py
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/braintree/exchange_rate_quote_request.py
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/braintree/liability_shift.py
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/braintree/local_payment_expired.py
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/braintree/local_payment_funded.py
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/braintree/local_payment_reversed.py
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/braintree/montary_amount.py
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/braintree/payment_method_customer_data_updated_metadata.py
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/braintree/sepa_direct_debit_account.py
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/braintree/sepa_direct_debit_account_gateway.py
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/braintree/test/authentication_ids.py
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/braintree/transaction_review.py
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/braintree/venmo_profile_data.py

Files in first set of .debs but not in second

-rw-r--r--  root/root   /usr/lib/python3/dist-packages/braintree-3.57.1.egg-info/PKG-INFO
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/braintree-3.57.1.egg-info/dependency_links.txt
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/braintree-3.57.1.egg-info/not-zip-safe
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/braintree-3.57.1.egg-info/requires.txt
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/braintree-3.57.1.egg-info/top_level.txt
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/braintree/coinbase_account.py
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/braintree/exceptions/down_for_maintenance_error.py
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/braintree/exceptions/forged_query_string_error.py
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/braintree/ideal_payment.py
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/braintree/ideal_payment_gateway.py
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/braintree/transparent_redirect.py
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/braintree/transparent_redirect_gateway.py
-rw-r--r--  root/root   /usr/share/doc/python3-braintree/changelog.gz

No differences were encountered in the control files

More details

Full run details