Skip to content

Commit

Permalink
4.24.0
Browse files Browse the repository at this point in the history
Co-authored-by: Debra Do <debdo@paypal.com>
Co-authored-by: Sara Vasquez <saravasquez@paypal.com>
  • Loading branch information
3 people committed Nov 7, 2023
1 parent 476df33 commit 5bf9af6
Show file tree
Hide file tree
Showing 27 changed files with 742 additions and 47 deletions.
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
# Changelog

## Unreleased
## 4.24.0
* Add `SubscriptionBillingSkipped` to `WebhookNotification.Kind`
* Add `arrivalDate` and `ticketIssuerAddress` to `Transaction.sale` request and `industry` data support for Transaction.submitForSettlement
* Add `date_of_birth` and `country_code` to IndustryData params
* Add `MetaCheckoutCard`, `MetaCheckoutToken` payment methods
* Add `MetaCheckoutCardDetails`, `MetaCheckoutTokenDetails` to `Transaction`
* Add `verification_add_ons` to `PaymentMethod` create options for `ACH NetworkCheck`
* Fix unittest compatibility with Python 3.12 (Thanks @mgorny)

## 4.23.0
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ The Braintree Python library provides integration access to the Braintree Gatewa

* [requests](http://docs.python-requests.org/en/latest/)

The Braintree Python SDK is tested against Python versions 3.5.3 and 3.8.0.
The Braintree Python SDK is tested against Python versions 3.5.3 and 3.12.0.

_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._

Expand Down
17 changes: 17 additions & 0 deletions braintree/meta_checkout_card.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import braintree
from braintree.address import Address
from braintree.resource import Resource

class MetaCheckoutCard(Resource):
def __init__(self, gateway, attributes):
Resource.__init__(self, gateway, attributes)

@property
def expiration_date(self):
if not self.expiration_month or not self.expiration_year:
return None
return self.expiration_month + "/" + self.expiration_year

@property
def masked_number(self):
return self.bin + "******" + self.last_4
17 changes: 17 additions & 0 deletions braintree/meta_checkout_token.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import braintree
from braintree.address import Address
from braintree.resource import Resource

class MetaCheckoutToken(Resource):
def __init__(self, gateway, attributes):
Resource.__init__(self, gateway, attributes)

@property
def expiration_date(self):
if not self.expiration_month or not self.expiration_year:
return None
return self.expiration_month + "/" + self.expiration_year

@property
def masked_number(self):
return self.bin + "******" + self.last_4
2 changes: 2 additions & 0 deletions braintree/payment_instrument_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ class PaymentInstrumentType():
EuropeBankAccount = "europe_bank_account"
LocalPayment = "local_payment"
MasterpassCard = "masterpass_card"
MetaCheckoutCard = "meta_checkout_card"
MetaCheckoutToken = "meta_checkout_token"
PayPalAccount = "paypal_account"
PayPalHere = "paypal_here"
SamsungPayCard = "samsung_pay_card"
Expand Down
2 changes: 2 additions & 0 deletions braintree/payment_method.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ def signature(type):
"skip_advanced_fraud_checking",
"us_bank_account_verification_method",
"verification_account_type",
"verification_add_ons",
"verification_amount",
"verification_merchant_account_id",
"verify_card",
Expand Down Expand Up @@ -123,6 +124,7 @@ def update_signature():
"us_bank_account_verification_method",
"venmo_sdk_session",
"verification_account_type",
"verification_add_ons",
"verification_amount",
"verification_merchant_account_id",
"verify_card",
Expand Down
3 changes: 3 additions & 0 deletions braintree/test/nonces.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ class Nonces(object):
MasterpassDiscover = "fake-masterpass-discover-nonce"
MasterpassMasterCard = "fake-masterpass-mastercard-nonce"
MasterpassVisa = "fake-masterpass-visa-nonce"
MetaCheckoutCard = "fake-meta-checkout-card-nonce"
MetaCheckoutToken = "fake-meta-checkout-token-nonce"
VisaCheckoutAmEx = "fake-visa-checkout-amex-nonce"
VisaCheckoutDiscover = "fake-visa-checkout-discover-nonce"
VisaCheckoutMasterCard = "fake-visa-checkout-mastercard-nonce"
Expand All @@ -79,3 +81,4 @@ class Nonces(object):
SamsungPayMasterCard = "tokensam_fake_mastercard"
SamsungPayVisa = "tokensam_fake_visa"
SepaDirectDebit = "fake-sepa-direct-debit-nonce"
UsBankAccount = "fake-us-bank-account-nonce"
54 changes: 53 additions & 1 deletion braintree/transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
from braintree.liability_shift import LiabilityShift
from braintree.local_payment import LocalPayment
from braintree.masterpass_card import MasterpassCard
from braintree.meta_checkout_card import MetaCheckoutCard
from braintree.meta_checkout_token import MetaCheckoutToken
from braintree.payment_instrument_type import PaymentInstrumentType
from braintree.paypal_account import PayPalAccount
from braintree.paypal_here import PayPalHere
Expand Down Expand Up @@ -640,7 +642,7 @@ def create_signature():
"data": [
"folio_number", "check_in_date", "check_out_date", "departure_date", "lodging_check_in_date", "lodging_check_out_date", "travel_package", "lodging_name", "room_rate",
"passenger_first_name", "passenger_last_name", "passenger_middle_initial", "passenger_title", "issued_date", "travel_agency_name", "travel_agency_code", "ticket_number",
"issuing_carrier_code", "customer_code", "fare_amount", "fee_amount", "room_tax", "tax_amount", "restricted_ticket", "no_show", "advanced_deposit", "fire_safe", "property_phone",
"issuing_carrier_code", "customer_code", "fare_amount", "fee_amount", "room_tax", "tax_amount", "restricted_ticket", "no_show", "advanced_deposit", "fire_safe", "property_phone", "arrival_date", "ticket_issuer_address", "date_of_birth", "country_code",
{
"legs": [
"conjunction_ticket", "exchange_ticket", "coupon_number", "service_class", "carrier_code", "fare_basis_code", "flight_number", "departure_date", "departure_airport_code", "departure_time",
Expand Down Expand Up @@ -678,6 +680,29 @@ def submit_for_settlement_signature():
"discount_amount",
"shipping_amount",
"ships_from_postal_code",
{"industry":
[
"industry_type",
{
"data": [
"advanced_deposit", "arrival_date", "check_in_date", "check_out_date", "customer_code", "departure_date", "fare_amount", "fee_amount", "fire_safe", "folio_number", "issued_date", "issuing_carrier_code",
"lodging_check_in_date", "lodging_check_out_date", "lodging_name", "no_show", "passenger_first_name", "passenger_last_name", "passenger_middle_initial", "passenger_title", "property_phone",
"restricted_ticket", "room_rate", "room_tax", "tax_amount", "ticket_issuer_address", "ticket_number", "travel_agency_code", "travel_agency_name", "travel_package",
{
"legs": [
"arrival_airport_code", "arrival_time", "carrier_code", "conjunction_ticket", "coupon_number", "departure_airport_code", "departure_date", "departure_time", "endorsement_or_restrictions",
"exchange_ticket", "fare_amount", "fare_basis_code", "fee_amount", "flight_number", "service_class", "stopover_permitted", "tax_amount"
]
},
{
"additional_charges": [
"amount", "kind"
],
}
]
}
]
},
{"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",
Expand All @@ -690,6 +715,29 @@ def submit_for_settlement_signature():
"postal_code", "region", "street_address",
]
},
{"industry":
[
"industry_type",
{
"data": [
"folio_number", "check_in_date", "check_out_date", "departure_date", "lodging_check_in_date", "lodging_check_out_date", "travel_package", "lodging_name", "room_rate",
"passenger_first_name", "passenger_last_name", "passenger_middle_initial", "passenger_title", "issued_date", "travel_agency_name", "travel_agency_code", "ticket_number",
"issuing_carrier_code", "customer_code", "fare_amount", "fee_amount", "room_tax", "tax_amount", "restricted_ticket", "no_show", "advanced_deposit", "fire_safe", "property_phone", "arrival_date", "ticket_issuer_address", "date_of_birth", "country_code",
{
"legs": [
"conjunction_ticket", "exchange_ticket", "coupon_number", "service_class", "carrier_code", "fare_basis_code", "flight_number", "departure_date", "departure_airport_code", "departure_time",
"arrival_airport_code", "arrival_time", "stopover_permitted", "fare_amount", "fee_amount", "tax_amount", "endorsement_or_restrictions"
]
},
{
"additional_charges": [
"kind", "amount"
],
}
]
}
]
},
]

@staticmethod
Expand Down Expand Up @@ -757,6 +805,10 @@ def __init__(self, gateway, 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 "meta_checkout_card" in attributes:
self.meta_checkout_card_details = MetaCheckoutCard(gateway, attributes.pop("meta_checkout_card"))
if "meta_checkout_token" in attributes:
self.meta_checkout_token_details = MetaCheckoutToken(gateway, attributes.pop("meta_checkout_token"))
if "sca_exemption_requested" in attributes:
self.sca_exemption_requested = attributes.pop("sca_exemption_requested")
else:
Expand Down
11 changes: 10 additions & 1 deletion braintree/us_bank_account_verification.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class Status(object):
# 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:
Constants representing verification types. Available types are:
* braintree.UsBankAccountVerification.VerificationMethod.NetworkCheck
* braintree.UsBankAccountVerification.VerificationMethod.IndependentCheck
Expand All @@ -40,6 +40,15 @@ class VerificationMethod(object):
TokenizedCheck = "tokenized_check"
MicroTransfers = "micro_transfers"

class VerificationAddOns(object):
"""
Constants representing verification add on types. Available statuses are:
* braintree.UsBankAccountVerification.VerificationAddOns.CustomerVerification
"""

CustomerVerification = "customer_verification"

def __init__(self, gateway, attributes):
AttributeGetter.__init__(self, attributes)

Expand Down
2 changes: 1 addition & 1 deletion braintree/version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Version = "4.23.0"
Version = "4.24.0"
1 change: 1 addition & 0 deletions braintree/webhook_notification.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ class Kind(object):
RecipientUpdatedGrantedPaymentMethod = "recipient_updated_granted_payment_method"
SubMerchantAccountApproved = "sub_merchant_account_approved"
SubMerchantAccountDeclined = "sub_merchant_account_declined"
SubscriptionBillingSkipped = "subscription_billing_skipped"
SubscriptionCanceled = "subscription_canceled"
SubscriptionChargedSuccessfully = "subscription_charged_successfully"
SubscriptionChargedUnsuccessfully = "subscription_charged_unsuccessfully"
Expand Down
26 changes: 26 additions & 0 deletions braintree/webhook_testing_gateway.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ def __subject_sample_xml(self, kind, id):
return self.__dispute_disputed_sample_xml(id)
elif kind == WebhookNotification.Kind.DisputeExpired:
return self.__dispute_expired_sample_xml(id)
elif kind == WebhookNotification.Kind.SubscriptionBillingSkipped:
return self.__subscription_billing_skipped_sample_xml(id)
elif kind == WebhookNotification.Kind.SubscriptionChargedSuccessfully:
return self.__subscription_charged_successfully_sample_xml(id)
elif kind == WebhookNotification.Kind.SubscriptionChargedUnsuccessfully:
Expand Down Expand Up @@ -750,6 +752,16 @@ def __subscription_sample_xml(self, id):
</subscription>
""" % id

def __subscription_billing_skipped_sample_xml(self, id):
return """
<subscription>
<id>%s</id>
<transactions type="array"></transactions>
<add_ons type="array"></add_ons>
<discounts type="array"></discounts>
</subscription>
""" % id

def __subscription_charged_successfully_sample_xml(self, id):
return """
<subscription>
Expand Down Expand Up @@ -984,6 +996,20 @@ def __payment_method_customer_data_updated_sample_xml(self, id):
<last-name>Doe</last-name>
<phone-number>1231231234</phone-number>
<email>john.doe@paypal.com</email>
<billing-address>
<street-address>Street Address</street-address>
<extended-address>Extended Address</extended-address>
<locality>Locality</locality>
<region>Region</region>
<postal-code >Postal Code</postal-code>
</billing-address>
<shipping-address>
<street-address>Street Address</street-address>
<extended-address>Extended Address</extended-address>
<locality>Locality</locality>
<region>Region</region>
<postal-code >Postal Code</postal-code>
</shipping-address>
</profile-data>
</enriched-customer-data>
</payment-method-customer-data-updated-metadata>
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

setup(
name="braintree",
version="4.23.0",
version="4.24.0",
description="Braintree Python Library",
long_description=long_description,
author="Braintree",
Expand Down
2 changes: 1 addition & 1 deletion tests/integration/test_credit_card.py
Original file line number Diff line number Diff line change
Expand Up @@ -1207,7 +1207,7 @@ def test_card_without_card_type_indicators(self):
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)
self.assertEqual(CreditCard.ProductId.Unknown, credit_card.product_id)

def test_card_with_account_type_debit(self):
customer = Customer.create().customer
Expand Down
25 changes: 12 additions & 13 deletions tests/integration/test_dispute_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ def create_sample_disputed_transaction(self):
"expiration_date": "12/2019",
},
"customer_id": customer.id,
"merchant_account_id": "14LaddersLLC_instant",
"options": {
"submit_for_settlement": True,
},
Expand All @@ -32,7 +31,7 @@ def test_advanced_search_no_results(self):
])

disputes = [dispute for dispute in collection.disputes.items]
self.assertEquals(0, len(disputes))
self.assertEqual(0, len(disputes))

def test_advanced_search_returns_single_dispute_by_customer_id(self):
transaction = self.create_sample_disputed_transaction()
Expand All @@ -42,25 +41,25 @@ def test_advanced_search_returns_single_dispute_by_customer_id(self):
])

disputes = [dispute for dispute in collection.disputes.items]
self.assertEquals(1, len(disputes))
self.assertEqual(1, len(disputes))

dispute = disputes[0]

self.assertEquals(dispute.id, transaction.disputes[0].id)
self.assertEquals(dispute.status, Dispute.Status.Open)
self.assertEqual(dispute.id, transaction.disputes[0].id)
self.assertEqual(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))
self.assertEqual(1, len(disputes))

dispute = disputes[0]

self.assertEquals(dispute.id, "open_dispute")
self.assertEquals(dispute.status, Dispute.Status.Open)
self.assertEqual(dispute.id, "open_dispute")
self.assertEqual(dispute.status, Dispute.Status.Open)

def test_advanced_search_returns_disputes_by_multiple_reasons(self):
collection = Dispute.search([
Expand Down Expand Up @@ -119,7 +118,7 @@ def test_advanced_search_returns_disputes_by_date_range(self):
disputes = [dispute for dispute in collection.disputes.items]
self.assertGreaterEqual(len(disputes), 1)

self.assertEquals(disputes[0].received_date, date(2014, 3, 4))
self.assertEqual(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()
Expand All @@ -132,7 +131,7 @@ def test_advanced_search_returns_disputes_by_disbursement_date_range(self):
disputes = [dispute for dispute in collection.disputes.items]
self.assertGreaterEqual(len(disputes), 1)

self.assertEquals(disputes[0].status_history[0].disbursement_date, disbursement_date)
self.assertEqual(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()
Expand All @@ -145,7 +144,7 @@ def test_advanced_search_returns_disputes_by_effective_date_range(self):
disputes = [dispute for dispute in collection.disputes.items]
self.assertGreaterEqual(len(disputes), 1)

self.assertEquals(disputes[0].status_history[0].effective_date, effective_date)
self.assertEqual(disputes[0].status_history[0].effective_date, effective_date)

def test_advanced_search_returns_disputes_by_amount_and_status(self):
collection = Dispute.search([
Expand All @@ -154,12 +153,12 @@ def test_advanced_search_returns_disputes_by_amount_and_status(self):
])

disputes = [dispute for dispute in collection.disputes.items]
self.assertEquals(1, len(disputes))
self.assertEqual(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))
self.assertEqual(0, len(disputes))
Loading

0 comments on commit 5bf9af6

Please sign in to comment.