Exemplo n.º 1
0
 def test_credit_card_page_shows_card_failing(self):
     ExchangeRoute.from_network(self.janet,
                                'balanced-cc').update_error('Some error')
     expected = 'Your credit card is <em id="status">failing'
     actual = self.client.GET('/janet/routes/credit-card.html',
                              auth_as='janet').body.decode('utf8')
     assert expected in actual
Exemplo n.º 2
0
    def test_giving_is_updated_when_credit_card_fails(self, btd):
        alice = self.make_participant("alice", claimed_time="now", last_bill_result="")
        team = self.make_team(is_approved=True)

        alice.set_payment_instruction(team, "5.00")  # funded

        assert alice.giving == Decimal("5.00")
        assert Team.from_slug(team.slug).receiving == Decimal("5.00")

        ExchangeRoute.from_network(alice, "braintree-cc").update_error("Card expired")

        assert Participant.from_username("alice").giving == Decimal("0.00")
        assert Team.from_slug(team.slug).receiving == Decimal("0.00")
    def test_giving_is_updated_when_credit_card_is_updated(self, btd):
        alice = self.make_participant('alice', claimed_time='now', last_bill_result='fail')
        team = self.make_team(is_approved=True)

        alice.set_payment_instruction(team, '5.00') # Not funded, failing card

        assert alice.giving == Decimal('0.00')
        assert Team.from_slug(team.slug).receiving == Decimal('0.00')

        # Alice updates her card..
        ExchangeRoute.from_network(alice, 'braintree-cc').invalidate()
        ExchangeRoute.insert(alice, 'braintree-cc', '/cards/bar', '')

        assert alice.giving == Decimal('5.00')
        assert Team.from_slug(team.slug).receiving == Decimal('5.00')
    def test_giving_is_updated_when_credit_card_fails(self, btd):
        alice = self.make_participant('alice', claimed_time='now', last_bill_result='')
        team = self.make_team(is_approved=True)

        alice.set_payment_instruction(team, '5.00') # funded

        assert alice.giving == Decimal('5.00')
        assert Team.from_slug(team.slug).receiving == Decimal('5.00')
        assert Participant.from_username(team.owner).taking == Decimal('5.00')

        ExchangeRoute.from_network(alice, 'braintree-cc').update_error("Card expired")

        assert Participant.from_username('alice').giving == Decimal('0.00')
        assert Team.from_slug(team.slug).receiving == Decimal('0.00')
        assert Participant.from_username(team.owner).taking == Decimal('0.00')
Exemplo n.º 5
0
    def test_giving_is_updated_when_credit_card_is_updated(self, btd):
        alice = self.make_participant("alice", claimed_time="now", last_bill_result="fail")
        team = self.make_team(is_approved=True)

        alice.set_payment_instruction(team, "5.00")  # Not funded, failing card

        assert alice.giving == Decimal("0.00")
        assert Team.from_slug(team.slug).receiving == Decimal("0.00")

        # Alice updates her card..
        ExchangeRoute.from_network(alice, "braintree-cc").invalidate()
        ExchangeRoute.insert(alice, "braintree-cc", "/cards/bar", "")

        assert alice.giving == Decimal("5.00")
        assert Team.from_slug(team.slug).receiving == Decimal("5.00")
Exemplo n.º 6
0
    def test_giving_is_updated_when_credit_card_is_updated(self, btd):
        alice = self.make_participant('alice', claimed_time='now', last_bill_result='fail')
        team = self.make_team(is_approved=True)

        alice.set_payment_instruction(team, '5.00') # Not funded, failing card

        assert alice.giving == Decimal('0.00')
        assert Team.from_slug(team.slug).receiving == Decimal('0.00')

        # Alice updates her card..
        ExchangeRoute.from_network(alice, 'braintree-cc').invalidate()
        ExchangeRoute.insert(alice, 'braintree-cc', '/cards/bar', '')

        assert alice.giving == Decimal('5.00')
        assert Team.from_slug(team.slug).receiving == Decimal('5.00')
Exemplo n.º 7
0
    def test_giving_is_updated_when_credit_card_fails(self, btd):
        alice = self.make_participant('alice', claimed_time='now', last_bill_result='')
        team = self.make_team(is_approved=True)

        alice.set_payment_instruction(team, '5.00') # funded

        assert alice.giving == Decimal('5.00')
        assert Team.from_slug(team.slug).receiving == Decimal('5.00')
        assert Participant.from_username(team.owner).taking == Decimal('5.00')

        ExchangeRoute.from_network(alice, 'braintree-cc').update_error("Card expired")

        assert Participant.from_username('alice').giving == Decimal('0.00')
        assert Team.from_slug(team.slug).receiving == Decimal('0.00')
        assert Participant.from_username(team.owner).taking == Decimal('0.00')
Exemplo n.º 8
0
 def test_credit_callback(self, rer):
     alice = self.make_participant('alice', last_ach_result='')
     ba = ExchangeRoute.from_network(alice, 'balanced-ba')
     for status in ('succeeded', 'failed'):
         error = 'FOO' if status == 'failed' else None
         e_id = record_exchange(self.db, ba, 10, 0, alice, 'pre')
         body = json.dumps({
             "events": [{
                 "type": "credit." + status,
                 "entity": {
                     "credits": [{
                         "failure_reason": error,
                         "meta": {
                             "participant_id": alice.id,
                             "exchange_id": e_id,
                         },
                         "status": status,
                     }]
                 }
             }]
         })
         r = self.callback(body=body, csrf_token=False)
         assert r.code == 200, r.body
         assert rer.call_count == 1
         assert rer.call_args[0][:-1] == (self.db, e_id, status, error)
         assert rer.call_args[0][-1].id == alice.id
         assert rer.call_args[1] == {}
         rer.reset_mock()
 def test_re_result_restores_balance_on_error(self):
     alice = self.make_participant('alice', balance=30, last_paypal_result='')
     ba = ExchangeRoute.from_network(alice, 'paypal')
     e_id = record_exchange(self.db, ba, D('-27.06'), D('0.81'), alice, 'pre')
     assert alice.balance == D('02.13')
     record_exchange_result(self.db, e_id, 'failed', 'SOME ERROR', alice)
     assert P('alice').balance == D('30.00')
 def test_re_result_updates_balance_for_positive_amounts(self):
     alice = self.make_participant('alice', balance=4, last_bill_result='')
     cc = ExchangeRoute.from_network(alice, 'braintree-cc')
     e_id = record_exchange(self.db, cc, D('31.59'), D('0.01'), alice, 'pre')
     assert alice.balance == D('4.00')
     record_exchange_result(self.db, e_id, 'succeeded', None, alice)
     assert P('alice').balance == D('35.59')
Exemplo n.º 11
0
 def test_associate_paypal(self, mailer):
     mailer.return_value = 1 # Email successfully sent
     self.roman.add_email('*****@*****.**')
     self.db.run("UPDATE emails SET verified=true WHERE address='*****@*****.**'")
     self.hit('roman', 'associate', 'paypal', '*****@*****.**')
     assert ExchangeRoute.from_network(self.roman, 'paypal')
     assert self.roman.has_payout_route
 def test_re_result_restores_balance_on_error(self):
     alice = self.make_participant('alice', balance=30, last_paypal_result='')
     ba = ExchangeRoute.from_network(alice, 'paypal')
     e_id = record_exchange(self.db, ba, D('-27.06'), D('0.81'), alice, 'pre')
     assert alice.balance == D('02.13')
     record_exchange_result(self.db, e_id, 'failed', 'SOME ERROR', alice)
     assert P('alice').balance == D('30.00')
Exemplo n.º 13
0
    def record_an_exchange(self, data, make_participants=True):
        if make_participants:
            self.make_participants()

        data.setdefault('status', 'succeeded')
        data.setdefault('note', 'noted')

        if 'route_id' not in data:
            try:
                data['route_id'] = ExchangeRoute.insert(
                    self.bob, 'paypal', '*****@*****.**').id
            except IntegrityError:
                data['route_id'] = ExchangeRoute.from_network(
                    self.bob, 'paypal').id

        if data['status'] is None:
            del (data['status'])
        if data['route_id'] is None:
            del (data['route_id'])

        if 'ref' not in data:
            data['ref'] = 'N/A'

        return self.client.PxST('/~bob/history/record-an-exchange',
                                data,
                                auth_as='alice')
 def test_re_result_updates_balance_for_positive_amounts(self):
     alice = self.make_participant('alice', balance=4, last_bill_result='')
     cc = ExchangeRoute.from_network(alice, 'braintree-cc')
     e_id = record_exchange(self.db, cc, D('31.59'), D('0.01'), alice, 'pre')
     assert alice.balance == D('4.00')
     record_exchange_result(self.db, e_id, 'succeeded', None, alice)
     assert P('alice').balance == D('35.59')
 def test_re_result_doesnt_restore_balance_on_success(self):
     alice = self.make_participant('alice', balance=50, last_paypal_result='')
     ba = ExchangeRoute.from_network(alice, 'paypal')
     e_id = record_exchange(self.db, ba, D('-43.98'), D('1.60'), alice, 'pre')
     assert alice.balance == D('4.42')
     record_exchange_result(self.db, e_id, 'succeeded', None, alice)
     assert P('alice').balance == D('4.42')
Exemplo n.º 16
0
 def test_credit_callback(self, rer):
     alice = self.make_participant('alice')
     ExchangeRoute.insert(alice, 'balanced-ba', '/bank/foo', '')
     ba = ExchangeRoute.from_network(alice, 'balanced-ba')
     for status in ('succeeded', 'failed'):
         error = 'FOO' if status == 'failed' else None
         e_id = record_exchange(self.db, ba, 10, 0, alice, 'pre')
         body = json.dumps({
             "events": [
                 {
                     "type": "credit."+status,
                     "entity": {
                         "credits": [
                             {
                                 "failure_reason": error,
                                 "meta": {
                                     "participant_id": alice.id,
                                     "exchange_id": e_id,
                                 },
                                 "status": status,
                             }
                         ]
                     }
                 }
             ]
         })
         r = self.callback(body=body, csrf_token=False)
         assert r.code == 200, r.body
         assert rer.call_count == 1
         assert rer.call_args[0][:-1] == (self.db, e_id, status, error)
         assert rer.call_args[0][-1].id == alice.id
         assert rer.call_args[1] == {}
         rer.reset_mock()
 def test_re_result_doesnt_restore_balance_on_success(self):
     alice = self.make_participant('alice', balance=50, last_paypal_result='')
     ba = ExchangeRoute.from_network(alice, 'paypal')
     e_id = record_exchange(self.db, ba, D('-43.98'), D('1.60'), alice, 'pre')
     assert alice.balance == D('4.42')
     record_exchange_result(self.db, e_id, 'succeeded', None, alice)
     assert P('alice').balance == D('4.42')
Exemplo n.º 18
0
def create_card_hold(db, participant, amount):
    """Create a hold on the participant's credit card.

    Amount should be the nominal amount. We'll compute Gratipay's fee below
    this function and add it to amount to end up with charge_amount.

    """
    typecheck(amount, Decimal)

    username = participant.username


    # Perform some last-minute checks.
    # ================================

    if participant.is_suspicious is not False:
        raise NotWhitelisted      # Participant not trusted.

    route = ExchangeRoute.from_network(participant, 'braintree-cc')
    if not route:
        return None, 'No credit card'


    # Go to Braintree.
    # ================

    cents, amount_str, charge_amount, fee = _prep_hit(amount)
    amount = charge_amount - fee
    msg = "Holding " + amount_str + " on Braintree for " + username + " ... "

    hold = None
    error = ""
    try:
        result = braintree.Transaction.sale({
            'amount': str(cents/100.0),
            'customer_id': route.participant.braintree_customer_id,
            'payment_method_token': route.address,
            'options': { 'submit_for_settlement': False },
            'custom_fields': {'participant_id': participant.id}
        })

        if result.is_success and result.transaction.status == 'authorized':
            error = ""
            hold = result.transaction
        elif result.is_success:
            error = "Transaction status was %s" % result.transaction.status
        else:
            error = result.message

    except Exception as e:
        error = repr_exception(e)

    if error == '':
        log(msg + "succeeded.")
    else:
        log(msg + "failed: %s" % error)
        record_exchange(db, route, amount, fee, participant, 'failed', error)

    return hold, error
Exemplo n.º 19
0
 def test_associate_paypal(self, mailer):
     mailer.return_value = 1  # Email successfully sent
     self.roman.add_email('*****@*****.**')
     self.db.run(
         "UPDATE emails SET verified=true WHERE address='*****@*****.**'")
     self.hit('roman', 'associate', 'paypal', '*****@*****.**')
     assert ExchangeRoute.from_network(self.roman, 'paypal')
     assert self.roman.has_payout_route
Exemplo n.º 20
0
 def credit_card_expiring(self):
     route = ExchangeRoute.from_network(self, 'braintree-cc')
     if not route:
         return
     card = CreditCard.from_route(route)
     year, month = card.expiration_year, card.expiration_month
     if not (year and month):
         return False
     return is_card_expiring(int(year), int(month))
Exemplo n.º 21
0
 def test_re_doesnt_update_balance_for_positive_amounts(self):
     alice = self.make_participant('alice', last_bill_result='')
     record_exchange(self.db,
                     ExchangeRoute.from_network(alice, 'braintree-cc'),
                     amount=D("0.59"),
                     fee=D("0.41"),
                     participant=alice,
                     status='pre')
     assert P('alice').balance == D('0.00')
 def test_associate_paypal_invalid(self):
     r = self.hit('roman',
                  'associate',
                  'paypal',
                  '*****@*****.**',
                  expected=400)
     assert not ExchangeRoute.from_network(self.roman, 'paypal')
     assert not self.roman.has_payout_route
     assert "Only verified email addresses allowed." in r.body
    def test_associate_and_delete_valid_paypal(self):
        self.add_and_verify_email(self.roman, '*****@*****.**')

        self.hit('roman', 'associate', 'paypal', '*****@*****.**')
        assert ExchangeRoute.from_network(self.roman, 'paypal')
        assert self.roman.has_payout_route

        self.hit('roman', 'delete', 'paypal', '*****@*****.**')
        assert not self.roman.has_payout_route
    def test_associate_and_delete_valid_paypal(self):
        self.add_and_verify_email(self.roman, '*****@*****.**')

        self.hit('roman', 'associate', 'paypal', '*****@*****.**')
        assert ExchangeRoute.from_network(self.roman, 'paypal')
        assert self.roman.has_payout_route

        self.hit('roman', 'delete', 'paypal', '*****@*****.**')
        assert not self.roman.has_payout_route
Exemplo n.º 25
0
 def test_re_requires_valid_route(self):
     alice = self.make_participant('alice', last_bill_result='')
     bob = self.make_participant('bob', last_bill_result='')
     with self.assertRaises(AssertionError):
         record_exchange(self.db,
                         ExchangeRoute.from_network(bob, 'braintree-cc'),
                         amount=D("0.59"),
                         fee=D("0.41"),
                         participant=alice,
                         status='pre')
Exemplo n.º 26
0
 def make_exchange(self, route, amount, fee, participant, status='succeeded', error=''):
     if not isinstance(route, ExchangeRoute):
         network = route
         route = ExchangeRoute.from_network(participant, network)
         if not route:
             route = ExchangeRoute.insert(participant, network, 'dummy-address')
             assert route
     e_id = record_exchange(self.db, route, amount, fee, participant, 'pre')
     record_exchange_result(self.db, e_id, status, error, participant)
     return e_id
 def test_re_result_restores_balance_on_error_with_invalidated_route(self):
     alice = self.make_participant('alice', balance=37, last_paypal_result='')
     pp = ExchangeRoute.from_network(alice, 'paypal')
     e_id = record_exchange(self.db, pp, D('-32.45'), D('0.86'), alice, 'pre')
     assert alice.balance == D('3.69')
     pp.update_error('invalidated')
     record_exchange_result(self.db, e_id, 'failed', 'oops', alice)
     alice = P('alice')
     assert alice.balance == D('37.00')
     assert pp.error == alice.get_paypal_error() == 'invalidated'
 def test_re_result_restores_balance_on_error_with_invalidated_route(self):
     alice = self.make_participant('alice', balance=37, last_paypal_result='')
     pp = ExchangeRoute.from_network(alice, 'paypal')
     e_id = record_exchange(self.db, pp, D('-32.45'), D('0.86'), alice, 'pre')
     assert alice.balance == D('3.69')
     pp.update_error('invalidated')
     record_exchange_result(self.db, e_id, 'failed', 'oops', alice)
     alice = P('alice')
     assert alice.balance == D('37.00')
     assert pp.error == alice.get_paypal_error() == 'invalidated'
    def test_add_new_paypal_address(self):
        self.add_and_verify_email(self.roman, '*****@*****.**')
        self.add_and_verify_email(self.roman, '*****@*****.**')

        self.hit('roman', 'associate', 'paypal', '*****@*****.**')
        self.hit('roman', 'delete', 'paypal', '*****@*****.**')
        self.hit('roman', 'associate', 'paypal', '*****@*****.**')

        assert self.roman.has_payout_route
        assert ExchangeRoute.from_network(self.roman, 'paypal').address == '*****@*****.**'
 def test_re_updates_balance_for_negative_amounts(self):
     alice = self.make_participant('alice', balance=50, last_paypal_result='')
     record_exchange( self.db
                    , ExchangeRoute.from_network(alice, 'paypal')
                    , amount=D('-35.84')
                    , fee=D('0.75')
                    , participant=alice
                    , status='pre'
                     )
     assert P('alice').balance == D('13.41')
 def test_record_exchange_result_restores_balance_on_error_with_invalidated_route(self):
     alice = self.make_participant('alice', balance=37, last_ach_result='')
     ba = ExchangeRoute.from_network(alice, 'balanced-ba')
     e_id = record_exchange(self.db, ba, D('-32.45'), D('0.86'), alice, 'pre')
     assert alice.balance == D('3.69')
     ba.update_error('invalidated')
     record_exchange_result(self.db, e_id, 'failed', 'oops', alice)
     alice = Participant.from_username('alice')
     assert alice.balance == D('37.00')
     assert ba.error == alice.get_bank_account_error() == 'invalidated'
 def test_re_updates_balance_for_negative_amounts(self):
     alice = self.make_participant('alice', balance=50, last_paypal_result='')
     record_exchange( self.db
                    , ExchangeRoute.from_network(alice, 'paypal')
                    , amount=D('-35.84')
                    , fee=D('0.75')
                    , participant=alice
                    , status='pre'
                     )
     assert P('alice').balance == D('13.41')
Exemplo n.º 33
0
 def make_exchange(self, route, amount, fee, participant, status='succeeded', error=''):
     if not isinstance(route, ExchangeRoute):
         network = route
         route = ExchangeRoute.from_network(participant, network)
         if not route:
             route = ExchangeRoute.insert(participant, network, 'dummy-address')
             assert route
     e_id = record_exchange(self.db, route, amount, fee, participant, 'pre')
     record_exchange_result(self.db, e_id, status, error, participant)
     return e_id
 def test_re_doesnt_update_balance_for_positive_amounts(self):
     alice = self.make_participant('alice', last_bill_result='')
     record_exchange( self.db
                    , ExchangeRoute.from_network(alice, 'braintree-cc')
                    , amount=D("0.59")
                    , fee=D("0.41")
                    , participant=alice
                    , status='pre'
                     )
     assert P('alice').balance == D('0.00')
 def test_re_requires_valid_route(self):
     alice = self.make_participant('alice', last_bill_result='')
     bob = self.make_participant('bob', last_bill_result='')
     with self.assertRaises(AssertionError):
         record_exchange( self.db
                        , ExchangeRoute.from_network(bob, 'braintree-cc')
                        , amount=D("0.59")
                        , fee=D("0.41")
                        , participant=alice
                        , status='pre'
                         )
 def test_re_records_exchange(self):
     alice = self.make_participant('alice', last_bill_result='')
     record_exchange( self.db
                    , ExchangeRoute.from_network(alice, 'braintree-cc')
                    , amount=D("0.59")
                    , fee=D("0.41")
                    , participant=alice
                    , status='pre'
                     )
     actual = self.db.one("""
         SELECT amount, fee, participant, status, route
           FROM exchanges
     """, back_as=dict)
     expected = { "amount": D('0.59')
                , "fee": D('0.41')
                , "participant": "alice"
                , "status": 'pre'
                , "route": ExchangeRoute.from_network(alice, 'braintree-cc').id
                 }
     assert actual == expected
 def test_re_records_exchange(self):
     alice = self.make_participant('alice', last_bill_result='')
     record_exchange( self.db
                    , ExchangeRoute.from_network(alice, 'braintree-cc')
                    , amount=D("0.59")
                    , fee=D("0.41")
                    , participant=alice
                    , status='pre'
                     )
     actual = self.db.one("""
         SELECT amount, fee, participant, status, route
           FROM exchanges
     """, back_as=dict)
     expected = { "amount": D('0.59')
                , "fee": D('0.41')
                , "participant": "alice"
                , "status": 'pre'
                , "route": ExchangeRoute.from_network(alice, 'braintree-cc').id
                 }
     assert actual == expected
    def test_add_new_paypal_address(self):
        self.add_and_verify_email(self.roman, '*****@*****.**')
        self.add_and_verify_email(self.roman, '*****@*****.**')

        self.hit('roman', 'associate', 'paypal', '*****@*****.**')
        self.hit('roman', 'delete', 'paypal', '*****@*****.**')
        self.hit('roman', 'associate', 'paypal', '*****@*****.**')

        assert self.roman.has_payout_route
        assert ExchangeRoute.from_network(
            self.roman, 'paypal').address == '*****@*****.**'
Exemplo n.º 39
0
 def test_re_stores_error_in_note(self):
     alice = self.make_participant('alice', last_bill_result='')
     record_exchange(self.db,
                     ExchangeRoute.from_network(alice, 'braintree-cc'),
                     amount=D("0.59"),
                     fee=D("0.41"),
                     participant=alice,
                     status='pre',
                     error='Card payment failed')
     exchange = self.db.one("SELECT * FROM exchanges")
     assert exchange.note == 'Card payment failed'
Exemplo n.º 40
0
 def make_exchange(self, route, amount, fee, participant, status='succeeded', error=''):
     if not isinstance(route, ExchangeRoute):
         network = route
         route = ExchangeRoute.from_network(participant, network)
         if not route:
             from .balanced import BalancedHarness
             route = ExchangeRoute.insert(participant, network, BalancedHarness.card_href)
             assert route
     e_id = record_exchange(self.db, route, amount, fee, participant, 'pre')
     record_exchange_result(self.db, e_id, status, error, participant)
     return e_id
Exemplo n.º 41
0
 def test_record_exchange_result_restores_balance_on_error_with_invalidated_route(
         self):
     alice = self.make_participant('alice', balance=37, last_ach_result='')
     ba = ExchangeRoute.from_network(alice, 'balanced-ba')
     e_id = record_exchange(self.db, ba, D('-32.45'), D('0.86'), alice,
                            'pre')
     assert alice.balance == D('3.69')
     ba.update_error('invalidated')
     record_exchange_result(self.db, e_id, 'failed', 'oops', alice)
     alice = Participant.from_username('alice')
     assert alice.balance == D('37.00')
     assert ba.error == alice.get_bank_account_error() == 'invalidated'
 def test_re_stores_error_in_note(self):
     alice = self.make_participant('alice', last_bill_result='')
     record_exchange( self.db
                    , ExchangeRoute.from_network(alice, 'braintree-cc')
                    , amount=D("0.59")
                    , fee=D("0.41")
                    , participant=alice
                    , status='pre'
                    , error='Card payment failed'
                     )
     exchange = self.db.one("SELECT * FROM exchanges")
     assert exchange.note == 'Card payment failed'
Exemplo n.º 43
0
 def make_exchange(self, route, amount, fee, participant, status='succeeded', error='',
                                                ref='dummy-trans-id', address='dummy-address'):
     """Factory for exchanges.
     """
     if not isinstance(route, ExchangeRoute):
         network = route
         route = ExchangeRoute.from_network(participant, network)
         if not route:
             route = ExchangeRoute.insert(participant, network, address)
             assert route
     e_id = record_exchange(self.db, route, amount, fee, participant, 'pre', ref)
     record_exchange_result(self.db, e_id, status, error, participant)
     return e_id
Exemplo n.º 44
0
 def make_exchange(self, route, amount, fee, participant, status='succeeded', error='',
                                                ref='dummy-trans-id', address='dummy-address'):
     """Factory for exchanges.
     """
     if not isinstance(route, ExchangeRoute):
         network = route
         route = ExchangeRoute.from_network(participant, network)
         if not route:
             route = ExchangeRoute.insert(participant, network, address)
             assert route
     e_id = record_exchange(self.db, route, amount, fee, participant, 'pre', ref)
     record_exchange_result(self.db, e_id, status, error, participant)
     return e_id
 def test_success_records_exchange(self):
     self.record_an_exchange({'amount': '10', 'fee': '0.50'})
     expected = { "amount": D('10.00')
                , "fee": D('0.50')
                , "participant": "bob"
                , "recorder": "alice"
                , "note": "noted"
                , "route": ExchangeRoute.from_network(self.bob, 'paypal').id
                 }
     SQL = "SELECT amount, fee, participant, recorder, note, route " \
           "FROM exchanges"
     actual = self.db.one(SQL, back_as=dict)
     assert actual == expected
Exemplo n.º 46
0
def set_paypal_email(username='',
                     email='',
                     api_key_fragment='',
                     overwrite=False):
    """
    Usage:

    [gratipay] $ env/bin/invoke set_paypal_email --username=username [email protected] [--api-key-fragment=12e4s678] [--overwrite]
    """

    if not username or not email:
        print_help(set_paypal_email)
        sys.exit(1)

    if not os.environ.get('DATABASE_URL'):
        load_prod_envvars()

    if not api_key_fragment:
        first_eight = "unknown!"
    else:
        first_eight = api_key_fragment

    wireup.db(wireup.env())

    participant = Participant.from_username(username)
    if not participant:
        print("No Gratipay participant found with username '" + username + "'")
        sys.exit(2)

    route = ExchangeRoute.from_network(participant, 'paypal')

    # PayPal caps the MassPay fee at $20 for users outside the U.S., and $1 for
    # users inside the U.S. Most Gratipay users using PayPal are outside the U.S.
    # so we set to $20 and I'll manually adjust to $1 when running MassPay and
    # noticing that something is off.
    FEE_CAP = 20

    if route:
        print("PayPal email is already set to: " + route.address)
        if not overwrite:
            print("Not overwriting existing PayPal email.")
            sys.exit(3)

    if participant.api_key == None:
        assert first_eight == "None"
    else:
        assert participant.api_key[0:8] == first_eight

    print("Setting PayPal email for " + username + " to " + email)
    ExchangeRoute.insert(participant, 'paypal', email, fee_cap=FEE_CAP)
    print("All done.")
Exemplo n.º 47
0
 def test_success_records_exchange(self):
     self.record_an_exchange({'amount': '10', 'fee': '0.50'})
     expected = {
         "amount": Decimal('10.00'),
         "fee": Decimal('0.50'),
         "participant": "bob",
         "recorder": "alice",
         "note": "noted",
         "route": ExchangeRoute.from_network(self.bob, 'paypal').id
     }
     SQL = "SELECT amount, fee, participant, recorder, note, route " \
           "FROM exchanges"
     actual = self.db.one(SQL, back_as=dict)
     assert actual == expected
Exemplo n.º 48
0
def set_paypal_email(username='', email='', api_key_fragment='', overwrite=False):
    """
    Usage:

    [gratipay] $ env/bin/invoke set_paypal_email --username=username [email protected] [--api-key-fragment=12e4s678] [--overwrite]
    """

    if not username or not email:
        print_help(set_paypal_email)
        sys.exit(1)

    if not os.environ.get('DATABASE_URL'):
        load_prod_envvars()

    if not api_key_fragment:
        first_eight = "unknown!"
    else:
        first_eight = api_key_fragment

    wireup.db(wireup.env())

    participant = Participant.from_username(username)
    if not participant:
        print("No Gratipay participant found with username '" + username + "'")
        sys.exit(2)

    route = ExchangeRoute.from_network(participant, 'paypal')

    # PayPal caps the MassPay fee at $20 for users outside the U.S., and $1 for
    # users inside the U.S. Most Gratipay users using PayPal are outside the U.S.
    # so we set to $20 and I'll manually adjust to $1 when running MassPay and
    # noticing that something is off.
    FEE_CAP = 20

    if route:
        print("PayPal email is already set to: " + route.address)
        if not overwrite:
            print("Not overwriting existing PayPal email.")
            sys.exit(3)

    if participant.api_key == None:
        assert first_eight == "None"
    else:
        assert participant.api_key[0:8] == first_eight

    print("Setting PayPal email for " + username + " to " + email)
    ExchangeRoute.insert(participant, 'paypal', email, fee_cap=FEE_CAP)
    print("All done.")
Exemplo n.º 49
0
def create_card_hold(db, participant, amount):
    """Create a hold on the participant's credit card.

    Amount should be the nominal amount. We'll compute Gratipay's fee below
    this function and add it to amount to end up with charge_amount.

    """
    typecheck(amount, Decimal)

    username = participant.username


    # Perform some last-minute checks.
    # ================================

    if participant.is_suspicious is not False:
        raise NotWhitelisted      # Participant not trusted.

    route = ExchangeRoute.from_network(participant, 'balanced-cc')
    if not route:
        return None, 'No credit card'


    # Go to Balanced.
    # ===============

    cents, amount_str, charge_amount, fee = _prep_hit(amount)
    msg = "Holding " + amount_str + " on Balanced for " + username + " ... "

    hold = None
    try:
        card = thing_from_href('cards', route.address)
        hold = card.hold( amount=cents
                        , description=username
                        , meta=dict(participant_id=participant.id, state='new')
                         )
        log(msg + "succeeded.")
        error = ""
    except Exception as e:
        error = repr_exception(e)
        log(msg + "failed: %s" % error)
        record_exchange(db, route, amount, fee, participant, 'failed', error)

    return hold, error
    def record_an_exchange(self, data, make_participants=True):
        if make_participants:
            self.make_participants()

        data.setdefault('status', 'succeeded')
        data.setdefault('note', 'noted')

        if 'route_id' not in data:
            try:
                data['route_id'] = ExchangeRoute.insert(self.bob, 'paypal', '*****@*****.**').id
            except IntegrityError:
                data['route_id'] = ExchangeRoute.from_network(self.bob, 'paypal').id

        if data['status'] is None:
            del(data['status'])
        if data['route_id'] is None:
            del(data['route_id'])

        return self.client.PxST('/~bob/history/record-an-exchange', data, auth_as='alice')
Exemplo n.º 51
0
 def test_associate_bitcoin(self):
     addr = '17NdbrSGoUotzeGCcMMCqnFkEvLymoou9j'
     self.hit('roman', 'associate', 'bitcoin', addr)
     route = ExchangeRoute.from_network(self.roman, 'bitcoin')
     assert route.address == addr
     assert route.error == ''
Exemplo n.º 52
0
 def test_set_paypal_email(self):
     alice = self.make_participant('alice', api_key='abcdefgh')
     set_paypal_email(username='******', email='*****@*****.**', api_key_fragment=alice.api_key[0:8])
     route = ExchangeRoute.from_network(alice, 'paypal')
     assert route.address == '*****@*****.**'
Exemplo n.º 53
0
def ach_credit(db, participant, withhold, minimum_credit=MINIMUM_CREDIT):

    # Compute the amount to credit them.
    # ==================================
    # Leave money in Gratipay to cover their obligations next week (as these
    # currently stand).

    balance = participant.balance
    assert balance is not None, balance # sanity check
    amount = balance - withhold

    # Do some last-minute checks.
    # ===========================

    if amount <= 0:
        return      # Participant not owed anything.

    if amount < minimum_credit:
        also_log = ""
        if withhold > 0:
            also_log = " ($%s balance - $%s in obligations)"
            also_log %= (balance, withhold)
        log("Minimum payout is $%s. %s is only due $%s%s."
           % (minimum_credit, participant.username, amount, also_log))
        return      # Participant owed too little.

    if not participant.is_whitelisted:
        raise NotWhitelisted      # Participant not trusted.

    route = ExchangeRoute.from_network(participant, 'balanced-ba')
    if not route:
        return 'No bank account'


    # Do final calculations.
    # ======================

    credit_amount, fee = skim_credit(amount)
    cents = credit_amount * 100

    if withhold > 0:
        also_log = "$%s balance - $%s in obligations"
        also_log %= (balance, withhold)
    else:
        also_log = "$%s" % amount
    msg = "Crediting %s %d cents (%s - $%s fee = $%s) on Balanced ... "
    msg %= (participant.username, cents, also_log, fee, credit_amount)


    # Try to dance with Balanced.
    # ===========================

    e_id = record_exchange(db, route, -credit_amount, fee, participant, 'pre')
    meta = dict(exchange_id=e_id, participant_id=participant.id)
    try:
        ba = thing_from_href('bank_accounts', route.address)
        ba.credit(amount=cents, description=participant.username, meta=meta)
        record_exchange_result(db, e_id, 'pending', None, participant)
        log(msg + "succeeded.")
        error = ""
    except Exception as e:
        error = repr_exception(e)
        record_exchange_result(db, e_id, 'failed', error, participant)
        log(msg + "failed: %s" % error)

    return error
Exemplo n.º 54
0
 def test_associate_bitcoin_invalid(self):
     self.hit('roman', 'associate', 'bitcoin', '12345', expected=400)
     assert not ExchangeRoute.from_network(self.roman, 'bitcoin')
Exemplo n.º 55
0
def create_card_hold(db, participant, amount):
    """Create a hold on the participant's credit card.

    Amount should be the nominal amount. We'll compute Gratipay's fee below
    this function and add it to amount to end up with charge_amount.

    """
    typecheck(amount, Decimal)

    username = participant.username


    # Perform some last-minute checks.
    # ================================

    if participant.is_suspicious is not False:
        raise NotWhitelisted      # Participant not trusted.

    route = ExchangeRoute.from_network(participant, 'braintree-cc')
    if not route:
        return None, 'No credit card'


    # Go to Braintree.
    # ================

    cents, amount_str, charge_amount, fee = _prep_hit(amount)
    amount = charge_amount - fee
    msg = "Holding " + amount_str + " on Braintree for " + username + " ... "

    hold = None
    error = ""
    try:
        result = braintree.Transaction.sale({
            'amount': str(cents/100.0),
            'customer_id': route.participant.braintree_customer_id,
            'payment_method_token': route.address,
            'options': { 'submit_for_settlement': False },
            'custom_fields': {'participant_id': participant.id}
        })

        if result.is_success and result.transaction.status == 'authorized':
            log(msg + "succeeded.")
            error = ""
            hold = result.transaction
        elif result.is_success:
            error = "Transaction status was %s" % result.transaction.status
        else:
            error = result.message

        if error == '':
            log(msg + "succeeded.")
        else:
            log(msg + "failed: %s" % error)
            record_exchange(db, route, amount, fee, participant, 'failed', error)

    except Exception as e:
        error = repr_exception(e)
        log(msg + "failed: %s" % error)
        record_exchange(db, route, amount, fee, participant, 'failed', error)

    return hold, error
Exemplo n.º 56
0
 def test_associate_paypal_invalid(self):
     r = self.hit('roman', 'associate', 'paypal', '*****@*****.**', expected=400)
     assert not ExchangeRoute.from_network(self.roman, 'paypal')
     assert not self.roman.has_payout_route
     assert "Only verified email addresses allowed." in r.body
 def test_re_fails_if_negative_balance(self):
     alice = self.make_participant('alice', last_paypal_result='')
     ba = ExchangeRoute.from_network(alice, 'paypal')
     with pytest.raises(NegativeBalance):
         record_exchange(self.db, ba, D("-10.00"), D("0.41"), alice, 'pre')