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
Example #2
0
    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' )
Example #4
0
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()
Example #5
0
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']
    }