def setUp(self): self.app = create_app('TEST') self.app.testing = True self.test_client = self.app.test_client() self.parameters = { 'reference_number': '101', 'method_used': 'Check', 'date_of_method_used': '2018-07-12 00:00:00', 'given_to': 'NERF', 'transaction_type': 'Gift', 'gross_gift_amount': Decimal(10.00), 'transaction_status': 'Completed', 'new_user_email': '*****@*****.**', 'customer_id': 'customer_id', 'second_transaction_type': 'Deposit to Bank', 'bank_deposit_number': '<bank-deposit-number>' } with self.app.app_context(): database.reflect() database.drop_all() database.create_all() # Create some ultsys user data for the Ultsys endpoints wrapped in functions for mocking. create_ultsys_users() database.session.add_all(create_method_used()) database.session.commit() self.method_used_id = MethodUsedModel.get_method_used( 'name', self.parameters['method_used']).id
def setUp(self): self.app = create_app('TEST') self.app.testing = True self.test_client = self.app.test_client() self.parameters = { 'reference_number': 'braintree_reference_number', 'user_exists_id': 5, 'user_new_id': '67', 'customer_id': 'customer_id', 'method_used': 'Web Form Credit Card', 'given_to': 'NERF', 'transaction_status': 'Completed', 'transaction_type': 'Gift', 'gross_gift_amount': Decimal(25.00), 'fee': Decimal(0.00) } init_braintree_credentials(self.app) with self.app.app_context(): database.reflect() database.drop_all() database.create_all() # Create some ultsys user data for the Ultsys endpoints wrapped in functions for mocking. create_ultsys_users() database.session.add_all(create_method_used()) database.session.commit() self.method_used_id = MethodUsedModel.get_method_used( 'name', self.parameters['method_used']).id
def record_bounced_check( payload ): """A function for recording the details of a bounced check. The payload required for the bounced check has the following keys: payload = { "gift_id": 1, "user_id": 1234, "reference_number": "201", "amount": "10.00", "transaction_notes": "Some transaction notes." } The reference number will be most likely the check number. :param dict payload: A dictionary that provides information to make the reallocation. :return: """ gift_searchable_id = payload[ 'gift_searchable_id' ] try: gift_model = GiftModel.query.filter_by( searchable_id=gift_searchable_id ).one() # The first transaction created has the check amount. # The last has the current balance. gross_gift_amount = \ gift_model.transactions[ 0 ].gross_gift_amount - gift_model.transactions[ -1 ].gross_gift_amount except: raise AdminFindGiftPathError() # Make sure the gift exists and that it has method_used='Check'. # Do not modify the database if method_used is not cCheck. Handle with app.errorhandler(). method_used = MethodUsedModel.get_method_used( 'name', 'Check' ) if gift_model.method_used_id != method_used.id: raise ModelGiftImproperFieldError enacted_by_agent = AgentModel.get_agent( 'Staff Member', 'user_id', payload[ 'user_id' ] ) try: # If gift exists and method_used is a check, record thet the check bounced. date_in_utc = datetime.datetime.utcnow().strftime( '%Y-%m-%d %H:%M:%S' ) transaction_json = { 'gift_id': gift_model.id, 'date_in_utc': date_in_utc, 'enacted_by_agent_id': enacted_by_agent.id, 'type': 'Bounced', 'status': 'Completed', 'reference_number': payload[ 'reference_number' ], 'gross_gift_amount': gross_gift_amount, 'fee': Decimal( 0.00 ), 'notes': payload[ 'transaction_notes' ] } transaction = from_json( TransactionSchema(), transaction_json ) database.session.add( transaction.data ) database.session.commit() except: database.session.rollback() raise AdminTransactionModelPathError( where='parent' )
def get_gift_with_customer_id(customer_id): """Given a Braintree customer ID find its gift. :param customer_id: Braintree customer ID. :return: GiftModel for that customer ID. """ try: # Find if a gift exists with the customer ID. We only have to look at Online or administrative online sales. # The customer_id is renewed on each sale and so should be unique to that donation. id_online = MethodUsedModel.get_method_used('name', 'Web Form Credit Card').id id_credit_card = MethodUsedModel.get_method_used( 'name', 'Admin-Entered Credit Card').id gift_with_customer_id = GiftModel.query \ .filter( or_( GiftModel.method_used_id == id_online, GiftModel.method_used_id == id_credit_card ) ) \ .filter_by( customer_id=customer_id ) \ .first() return gift_with_customer_id except: # noqa: E722 raise AdminFindGiftPathError()
def get_payment_method_token(payload): """Create a payment method token and return. :param payload: The payload includes the user, gift, and transaction. :return: """ method_used = MethodUsedModel.get_method_used( 'name', payload['gift']['method_used']) braintree_customer = create_braintree_customer( payload['user'], method_used.billing_address_required) payload['user']['customer_id'] = braintree_customer.id # This was newly created and the only payment method associated with the customer. return braintree_customer.payment_methods[0].token
def create_method_used(): """Create the methods used. :return: The list of MethodUsedModels. """ method_used_models = list() method_used_models.append( MethodUsedModel(id=1, name='Web Form Credit Card', billing_address_required=1)) method_used_models.append( MethodUsedModel(id=2, name='Admin-Entered Credit Card', billing_address_required=1)) method_used_models.append( MethodUsedModel(id=3, name='Check', billing_address_required=0)) method_used_models.append( MethodUsedModel(id=4, name='Money Order', billing_address_required=0)) method_used_models.append( MethodUsedModel(id=5, name='Stock', billing_address_required=0)) method_used_models.append( MethodUsedModel(id=6, name='Cash', billing_address_required=0)) method_used_models.append( MethodUsedModel(id=7, name='Wire Transfer', billing_address_required=0)) method_used_models.append( MethodUsedModel(id=8, name='Other', billing_address_required=0)) method_used_models.append( MethodUsedModel(id=9, name='Web Form PayPal', billing_address_required=0)) method_used_models.append( MethodUsedModel(id=10, name='Web Form Venmo', billing_address_required=0)) method_used_models.append( MethodUsedModel(id=11, name='Web Form ApplePay', billing_address_required=0)) method_used_models.append( MethodUsedModel(id=12, name='Unkown Method Used', billing_address_required=0)) return method_used_models
def test_braintree_webhooks(self, mock_init_gateway_function, mock_subscription_function, get_ultsys_user_function): # pylint: disable=unused-argument """Make sure the webhook endpoint receives a payload and makes updates as expected.""" with self.app.app_context(): url = '/donation/webhook/braintree/subscription' # Create the sourced by agent for the subscription webhook. agent_model = from_json(AgentSchema(), get_agent_jsons()[0], create=True) database.session.add(agent_model.data) database.session.commit() # Here is the first gift as check. gift_dict = get_gift_dict({ 'user_id': 1, 'method_used': METHOD_USED, 'sourced_from_agent_id': 1, 'recurring_subscription_id': 'recurring_subscription_id' }) gift_model = from_json(GiftSchema(), gift_dict, create=True) database.session.add(gift_model.data) database.session.flush() # Create a transaction on the gift. transaction_dict = get_transaction_dict({ 'gift_id': gift_model.data.id, 'enacted_by_agent_id': agent_model.data.id, 'type': 'Gift', 'gross_gift_amount': Decimal('1.00') }) transaction_model = from_json(TransactionSchema(), transaction_dict, create=True) database.session.add(transaction_model.data) database.session.commit() # Here is the fake POST from Braintree when the subscription webhook is triggered. response = self.test_client.post( url, data={ 'bt_signature': 'bt_signature', 'bt_payload': 'subscription_charged_successfully' }) self.assertEqual(response.status_code, status.HTTP_200_OK) method_used_id = MethodUsedModel.get_method_used( 'name', METHOD_USED).id gift = GiftModel.query.filter_by(id=1).one_or_none() self.assertEqual(gift.method_used_id, method_used_id) self.assertEqual(gift.sourced_from_agent_id, SOURCED_FROM_AGENT) self.assertEqual(gift.recurring_subscription_id, RECURRING_SUBSCRIPTION_ID) transaction = TransactionModel.query.filter_by(id=1).one_or_none() self.assertEqual(transaction.gift_id, 1) self.assertEqual(transaction.type, 'Gift') self.assertEqual(transaction.status, 'Completed') response = self.test_client.post( url, data={ 'bt_signature': 'bt_signature', 'bt_payload': 'subscription_charged_unsuccessfully' }) self.assertEqual(response.status_code, status.HTTP_200_OK) transaction = TransactionModel.query.filter_by(id=3).one_or_none() self.assertEqual(transaction.status, 'Declined') response = self.test_client.post(url, data={ 'bt_signature': 'bt_signature', 'bt_payload': 'subscription_went_past_due' }) self.assertEqual(response.status_code, status.HTTP_200_OK) transaction = TransactionModel.query.filter_by(id=4).one_or_none() self.assertEqual(transaction.status, 'Failed') response = self.test_client.post(url, data={ 'bt_signature': 'bt_signature', 'bt_payload': 'subscription_expired' }) self.assertEqual(response.status_code, status.HTTP_200_OK) transaction = TransactionModel.query.filter_by(id=5).one_or_none() self.assertEqual(transaction.status, 'Failed')
def make_admin_sale(payload): """Use the payload to build an administrative donation. payload = { "gift": { "method_used": "Check", "given_to": "NERF", }, "transaction": { "date_of_method_used": "2018-07-12 00:00:00", "gross_gift_amount": "15.00", "reference_number": "1201", "bank_deposit_number": "<bank-deposit-number>", "type": "Gift", "notes": "A note for the transaction." }, "user": { "user_id": null, "user_address": { "user_first_name": "Ralph", "user_last_name": "Kramden", "user_zipcode": "11214", "user_address": "328 Chauncey St", "user_city": "Bensonhurst", "user_state": "NY", "user_email_address": "*****@*****.**", "user_phone_number": "9172307441" }, "billing_address": { "billing_first_name": "Ralph", "billing_last_name": "Kramden", "billing_zipcode": "11214", "billing_address": "7001 18th Ave", "billing_city": "Bensonhurst", "billing_state": "NY", "billing_email_address": "*****@*****.**", "billing_phone_number": "9172307441" } }, "payment_method_nonce": "fake-valid-visa-nonce", "recurring_subscription": false } Since there is one payload from the front-end, which must include 2 dates and 2 reference numbers, these are both included as separate key-value pairs in the payload. This gives us one submit from front to back-end. :param dict payload: The payload required to update the models as needed. :return dict: Returns transaction and gift dictionaries. """ # We don't want to do caging up front because it takes too long. Move to end of the sale in controller. # Assign a category: 'queued' and a user ID of -2 ( -1 is used for caged ) payload['user']['category'] = 'queued' payload['gift']['user_id'] = -2 # This is not a Braintree transaction and do set the Braintree customer ID to None. payload['user']['customer_id'] = '' sourced_from_agent = AgentModel.get_agent( 'Staff Member', 'user_id', payload['sourced_from_agent_user_id']) enacted_by_agent = sourced_from_agent method_used = MethodUsedModel.get_method_used( 'name', payload['gift']['method_used']) # Create the gift dictionary from admin payload. gift = { 'campaign_id': None, 'method_used_id': method_used.id if method_used else None, 'sourced_from_agent_id': sourced_from_agent.id if sourced_from_agent else None, 'given_to': payload['gift']['given_to'].upper(), 'recurring_subscription_id': None } # Create the transaction dictionary from the administrative payload. # If it is a check or money order add a second transaction to capture the date on the payment. transactions = [] utc_now = datetime.datetime.utcnow() transaction_type = payload['transaction']['type'] transaction_notes = payload['transaction']['notes'] method_used_date_note = 'Date given is date of method used. {}'.format( transaction_notes) fee = 0.00 if 'fee' in payload and payload['transaction']['fee']: fee = payload['transaction']['fee'] is_check_money_order = payload[ 'gift' ][ 'method_used' ] == 'Check' or\ payload[ 'gift' ][ 'method_used' ] == 'Money Order' transactions.append({ 'date_in_utc': payload['transaction']['date_of_method_used'], 'enacted_by_agent_id': enacted_by_agent.id if enacted_by_agent else None, 'type': transaction_type, 'status': 'Completed', 'reference_number': payload['transaction']['reference_number'], 'gross_gift_amount': payload['transaction']['gross_gift_amount'], 'fee': fee, 'notes': method_used_date_note if is_check_money_order else transaction_notes }) if is_check_money_order: bank_agent = AgentModel.get_agent('Organization', 'name', 'Fidelity Bank') bank_agent_id = bank_agent.id transactions.append({ 'date_in_utc': utc_now.strftime('%Y-%m-%d %H:%M:%S'), 'enacted_by_agent_id': bank_agent_id, 'type': 'Deposit to Bank', 'status': 'Completed', 'reference_number': payload['transaction']['bank_deposit_number'], 'gross_gift_amount': payload['transaction']['gross_gift_amount'], 'fee': fee, 'notes': '' }) return { 'transactions': transactions, 'gift': gift, 'user': payload['user'] }