From ce91e7efcd29f90047eca4a8a143053321ea89d1 Mon Sep 17 00:00:00 2001 From: Changaco Date: Fri, 20 Jun 2014 15:06:02 +0200 Subject: [PATCH 01/97] use temporary tables instead of making lots of expensive queries --- gittip/billing/payday.py | 207 ++++++++++++++++++-------------- gittip/models/participant.py | 8 +- tests/py/test_billing_payday.py | 154 +++--------------------- 3 files changed, 142 insertions(+), 227 deletions(-) diff --git a/gittip/billing/payday.py b/gittip/billing/payday.py index bfb026cc8b..c249ee8230 100644 --- a/gittip/billing/payday.py +++ b/gittip/billing/payday.py @@ -25,7 +25,6 @@ import aspen.utils from aspen import log from aspen.utils import typecheck -from gittip.models.participant import Participant from gittip.exceptions import NegativeBalance from psycopg2 import IntegrityError @@ -84,9 +83,6 @@ def __str__(self): return "No payday found where one was expected." -LOOP_PAYIN, LOOP_PACHINKO, LOOP_PAYOUT = range(3) - - class Payday(object): """Represent an abstract event during which money is moved. @@ -103,33 +99,6 @@ def __init__(self, db): self.db = db - def genparticipants(self, ts_start, loop): - """Generator to yield participants with extra info. - - The extra info varies depending on which loop we're in: tips/total for - payin and payout, takes for pachinko. - - """ - teams_only = (loop == LOOP_PACHINKO) - for participant in self.get_participants(ts_start, teams_only): - if loop == LOOP_PAYIN: - extra = participant.get_tips_and_total(for_payday=ts_start) - elif loop == LOOP_PACHINKO: - extra = participant.get_takes(for_payday=ts_start) - elif loop == LOOP_PAYOUT: - - # On the payout loop we want to use the total obligations they - # have for next week, and if we pass a non-False for_payday to - # get_tips_and_total then we only get unfulfilled tips from - # prior to that timestamp, which is none of them by definition - # at this point since we just recently finished payin. - - extra = participant.get_tips_and_total() - else: - raise Exception # sanity check - yield(participant, extra) - - def run(self): """This is the starting point for payday. @@ -143,13 +112,14 @@ def run(self): _start = aspen.utils.utcnow() log("Greetings, program! It's PAYDAY!!!!") ts_start = self.start() + self.prepare(ts_start) self.zero_out_pending(ts_start) - self.payin(ts_start, self.genparticipants(ts_start, loop=LOOP_PAYIN)) + self.payin(ts_start) self.move_pending_to_balance_for_teams() - self.pachinko(ts_start, self.genparticipants(ts_start, loop=LOOP_PACHINKO)) + self.pachinko(ts_start) self.clear_pending_to_balance() - self.payout(ts_start, self.genparticipants(ts_start, loop=LOOP_PAYOUT)) + self.payout() self.update_stats(ts_start) self.update_receiving_amounts() @@ -190,6 +160,88 @@ def start(self): return ts_start + def prepare(self, ts_start): + """Prepare the DB: we need temporary tables with indexes. + """ + self.db.run(""" + + DROP TABLE IF EXISTS pay_participants CASCADE; + CREATE TEMPORARY TABLE pay_participants AS + SELECT username + , claimed_time + , number + , is_suspicious + , balance + , balanced_customer_href + , last_bill_result + FROM participants + WHERE is_suspicious IS NOT true + AND claimed_time < %(ts_start)s + ORDER BY claimed_time; + + CREATE UNIQUE INDEX ON pay_participants (username); + + DROP TABLE IF EXISTS pay_transfers CASCADE; + CREATE TEMPORARY TABLE pay_transfers AS + SELECT * + FROM transfers t + WHERE t.timestamp > %(ts_start)s; + + DROP TABLE IF EXISTS pay_tips CASCADE; + CREATE TEMPORARY TABLE pay_tips AS + SELECT tipper, tippee, amount + FROM ( SELECT DISTINCT ON (tipper, tippee) * + FROM tips + WHERE mtime < %(ts_start)s + ORDER BY tipper, tippee, mtime DESC + ) t + JOIN pay_participants p ON p.username = t.tipper + WHERE t.amount > 0 + AND t.tippee IN (SELECT username FROM pay_participants) + AND ( SELECT id + FROM pay_transfers t2 + WHERE t.tipper = t2.tipper + AND t.tippee = t2.tippee + AND context = 'tip' + ) IS NULL + ORDER BY p.claimed_time ASC, t.ctime ASC; + + CREATE INDEX ON pay_tips (tipper); + CREATE INDEX ON pay_tips (tippee); + + ALTER TABLE pay_participants ADD COLUMN giving_today numeric(35,2); + UPDATE pay_participants + SET giving_today = ( + SELECT sum(amount) + FROM pay_tips + WHERE tipper = username + ); + + DROP TABLE IF EXISTS pay_takes CASCADE; + CREATE TEMPORARY TABLE pay_takes AS + SELECT team, member, amount, ctime + FROM ( SELECT DISTINCT ON (team, member) + team, member, amount, ctime + FROM takes + WHERE mtime < %(ts_start)s + ORDER BY team, member, mtime DESC + ) t + WHERE t.amount > 0 + AND t.team IN (SELECT username FROM pay_participants) + AND t.member IN (SELECT username FROM pay_participants) + AND ( SELECT id + FROM pay_transfers t2 + WHERE t.team = t2.tipper + AND t.member = t2.tippee + AND context = 'take' + ) IS NULL; + + CREATE INDEX ON pay_takes (team); + + """, dict(ts_start=ts_start)) + log('Prepared the DB.') + + def zero_out_pending(self, ts_start): """Given a timestamp, zero out the pending column. @@ -211,38 +263,27 @@ def zero_out_pending(self, ts_start): return None - def get_participants(self, ts_start, teams_only=False): - """Given a timestamp, return a list of participants dicts. - """ - PARTICIPANTS = """\ - SELECT participants.*::participants - FROM participants - WHERE claimed_time IS NOT NULL - AND claimed_time < %s - AND is_suspicious IS NOT true - {} - ORDER BY claimed_time ASC - """.format(teams_only and "AND number = 'plural'" or '') - participants = self.db.all(PARTICIPANTS, (ts_start,)) - log("Fetched participants.") - return participants - - - def payin(self, ts_start, participants): - """Given a datetime and an iterator, do the payin side of Payday. + def payin(self, ts_start): + """Given a datetime, do the payin side of Payday. """ i = 0 log("Starting payin loop.") - for i, (participant, (tips, total)) in enumerate(participants, start=1): + participants = self.db.all(""" + SELECT * FROM pay_participants WHERE giving_today > 0 + """) + for i, participant in enumerate(participants, start=1): if i % 100 == 0: log("Payin done for %d participants." % i) - self.charge_and_or_transfer(ts_start, participant, tips, total) + self.charge_and_or_transfer(ts_start, participant) log("Did payin for %d participants." % i) - def pachinko(self, ts_start, participants): + def pachinko(self, ts_start): i = 0 - for i, (participant, takes) in enumerate(participants, start=1): + participants = self.db.all(""" + SELECT * FROM pay_participants WHERE number = 'plural' + """) + for i, participant in enumerate(participants, start=1): if i % 100 == 0: log("Pachinko done for %d participants." % i) @@ -263,6 +304,10 @@ def tip(tippee, amount): , pachinko=True ) + takes = self.db.all(""" + SELECT * FROM pay_takes WHERE team = %s ORDER BY ctime DESC + """, (participant.username,), back_as=dict) + for take in takes: amount = min(take['amount'], available) available -= amount @@ -273,26 +318,30 @@ def tip(tippee, amount): log("Did pachinko for %d participants." % i) - def payout(self, ts_start, participants): - """Given a datetime and an iterator, do the payout side of Payday. + def payout(self): + """Do the payout side of Payday. """ i = 0 log("Starting payout loop.") - for i, (participant, (tips, total)) in enumerate(participants, start=1): + participants = self.db.all(""" + SELECT p.*::participants FROM participants p WHERE balance > 0 + """) + for i, participant in enumerate(participants, start=1): if i % 100 == 0: log("Payout done for %d participants." % i) - self.ach_credit(ts_start, participant, tips, total) + withhold = participant.giving + participant.pledging + self.ach_credit(participant, withhold) log("Did payout for %d participants." % i) - def charge_and_or_transfer(self, ts_start, participant, tips, total): + def charge_and_or_transfer(self, ts_start, participant): """Given one participant record, pay their day. Charge each participants' credit card if needed before transfering money between Gittip accounts. """ - short = total - participant.balance + short = participant.giving_today - participant.balance if short > 0: # The participant's Gittip account is short the amount needed to @@ -304,6 +353,10 @@ def charge_and_or_transfer(self, ts_start, participant, tips, total): self.charge(participant, short) + tips = self.db.all(""" + SELECT * FROM pay_tips WHERE tipper = %s + """, (participant.username,), back_as=dict) + nsuccessful_tips = 0 for tip in tips: result = self.tip(participant, tip, ts_start) @@ -312,8 +365,6 @@ def charge_and_or_transfer(self, ts_start, participant, tips, total): else: break - self.mark_participant() - def move_pending_to_balance_for_teams(self): """Transfer pending into balance for teams. @@ -401,6 +452,7 @@ def update_stats(self, ts_start): SELECT tippee FROM our_transfers ) AS foo ) + , nparticipants = (SELECT count(*) FROM pay_participants) , ntippers = (SELECT count(DISTINCT tipper) FROM our_transfers) , ntips = (SELECT count(*) FROM our_tips) , npachinko = (SELECT count(*) FROM our_pachinkos) @@ -481,16 +533,6 @@ def tip(self, participant, tip, ts_start, pachinko=False): return 0 - claimed_time = tip['claimed_time'] - if claimed_time is None or claimed_time > ts_start: - - # Gittip is opt-in. We're only going to collect money on a person's - # behalf if they opted-in by claiming their account before the - # start of this payday. - - log("SKIPPED: %s" % msg) - return 0 - if not self.transfer(participant.username, tip['tippee'], \ tip['amount'], pachinko=pachinko): @@ -584,7 +626,7 @@ def charge(self, participant, amount): function and add it to amount to end up with charge_amount. """ - typecheck(participant, Participant, amount, Decimal) + typecheck(amount, Decimal) username = participant.username balanced_customer_href = participant.balanced_customer_href @@ -624,7 +666,7 @@ def charge(self, participant, amount): ) - def ach_credit(self, ts_start, participant, tips, total, minimum_credit=MINIMUM_CREDIT): + def ach_credit(self, participant, total, minimum_credit=MINIMUM_CREDIT): # Compute the amount to credit them. # ================================== @@ -911,14 +953,3 @@ def mark_ach_failed(self, cursor): RETURNING id """, default=NoPayday) - - - def mark_participant(self): - self.db.one("""\ - - UPDATE paydays - SET nparticipants = nparticipants + 1 - WHERE ts_end='1970-01-01T00:00:00+00'::timestamptz - RETURNING id - - """, default=NoPayday) diff --git a/gittip/models/participant.py b/gittip/models/participant.py index f35d500df3..ae4d56adea 100644 --- a/gittip/models/participant.py +++ b/gittip/models/participant.py @@ -291,11 +291,9 @@ def withdraw_balance_to_bank_account(self, cursor): # Monkey-patch a couple methods, coopting them for callbacks, essentially. hack.mark_ach_failed = lambda cursor: None - hack.ach_credit( ts_start=None # not used - , participant=self - , tips=None # not used - , total=Decimal('0.00') # don't withold anything - , minimum_credit=Decimal('0.00') # send it all + hack.ach_credit( self + , Decimal('0.00') # don't withhold anything + , Decimal('0.00') # send it all ) # XXX Records the exchange using a different cursor. :-/ diff --git a/tests/py/test_billing_payday.py b/tests/py/test_billing_payday.py index 58eda78556..457c7e838b 100644 --- a/tests/py/test_billing_payday.py +++ b/tests/py/test_billing_payday.py @@ -1,7 +1,6 @@ from __future__ import absolute_import, division, print_function, unicode_literals from decimal import Decimal as D -from datetime import datetime import balanced import mock @@ -9,8 +8,7 @@ from psycopg2 import IntegrityError from aspen.utils import typecheck, utcnow -from gittip import billing -from gittip.billing.payday import Payday, skim_credit, LOOP_PACHINKO +from gittip.billing.payday import Payday, skim_credit from gittip.exceptions import NegativeBalance from gittip.models.participant import Participant from gittip.testing import Harness @@ -353,100 +351,6 @@ def test_update_receiving_amounts_includes_taking(self): assert Participant.from_username('A').receiving == 13 assert Participant.from_username('A').taking == 3 - @mock.patch('gittip.models.participant.Participant.get_tips_and_total') - def test_charge_and_or_transfer_no_tips(self, get_tips_and_total): - self.db.run(""" - UPDATE participants - SET balance=1 - WHERE username='janet' - """) - - amount = D('1.00') - - ts_start = self.payday.start() - - tips, total = [], amount - - initial_payday = self.fetch_payday() - self.payday.charge_and_or_transfer(ts_start, self.janet, tips, total) - resulting_payday = self.fetch_payday() - - assert initial_payday['ntippers'] == resulting_payday['ntippers'] - assert initial_payday['ntips'] == resulting_payday['ntips'] - assert initial_payday['nparticipants'] + 1 == resulting_payday['nparticipants'] - - @mock.patch('gittip.models.participant.Participant.get_tips_and_total') - @mock.patch('gittip.billing.payday.Payday.tip') - def test_charge_and_or_transfer(self, tip, get_tips_and_total): - self.db.run(""" - UPDATE participants - SET balance=1 - WHERE username='janet' - """) - - ts_start = self.payday.start() - now = datetime.utcnow() - amount = D('1.00') - like_a_tip = {'amount': amount, 'tippee': 'mjallday', 'ctime': now, - 'claimed_time': now} - - # success, success, claimed, failure - tips = [like_a_tip, like_a_tip, like_a_tip, like_a_tip] - total = amount - - ts_start = datetime.utcnow() - - return_values = [1, 1, 0, -1] - return_values.reverse() - - def tip_return_values(*_): - return return_values.pop() - - tip.side_effect = tip_return_values - - initial_payday = self.fetch_payday() - self.payday.charge_and_or_transfer(ts_start, self.janet, tips, total) - resulting_payday = self.fetch_payday() - - assert initial_payday['nparticipants'] + 1 == resulting_payday['nparticipants'] - - @mock.patch('gittip.models.participant.Participant.get_tips_and_total') - @mock.patch('gittip.billing.payday.Payday.charge') - def test_charge_and_or_transfer_short(self, charge, get_tips_and_total): - self.db.run(""" - UPDATE participants - SET balance=1 - WHERE username='janet' - """) - - now = datetime.utcnow() - amount = D('1.00') - like_a_tip = {'amount': amount, 'tippee': 'mjallday', 'ctime': now, - 'claimed_time': now} - - # success, success, claimed, failure - tips = [like_a_tip, like_a_tip, like_a_tip, like_a_tip] - get_tips_and_total.return_value = tips, amount - - ts_start = datetime.utcnow() - - # In real-life we wouldn't be able to catch an error as the charge - # method will swallow any errors and return false. We don't handle this - # return value within charge_and_or_transfer but instead continue on - # trying to use the remaining credit in the user's account to payout as - # many tips as possible. - # - # Here we're hacking the system and throwing the exception so execution - # stops since we're only testing this part of the method. That smells - # like we need to refactor. - - charge.side_effect = Exception() - with self.assertRaises(Exception): - billing.charge_and_or_transfer(ts_start, self.janet) - assert charge.called_with(self.janet.username, - self.janet_href, - amount) - @mock.patch('gittip.billing.payday.Payday.transfer') @mock.patch('gittip.billing.payday.log') def test_tip(self, log, transfer): @@ -481,18 +385,6 @@ def test_tip(self, log, transfer): tip['amount'] = amount - # XXX: We should have constants to compare the values to - # not claimed - tip['claimed_time'] = None - result = self.payday.tip(self.janet, tip, ts_start) - assert result == 0 - - # XXX: We should have constants to compare the values to - # claimed after payday - tip['claimed_time'] = utcnow() - result = self.payday.tip(self.janet, tip, ts_start) - assert result == 0 - ts_start = utcnow() # XXX: We should have constants to compare the values to @@ -502,21 +394,25 @@ def test_tip(self, log, transfer): assert result == -1 @mock.patch('gittip.billing.payday.log') - def test_start_zero_out_and_get_participants(self, log): + def test_start_prepare_and_zero_out(self, log): self.clear_tables() self.make_participant('bob', balance=10, claimed_time=None, pending=1) self.make_participant('carl', balance=10, claimed_time='now', pending=1) ts_start = self.payday.start() + get_participants = lambda: self.db.all("SELECT * FROM pay_participants") + + self.payday.prepare(ts_start) self.payday.zero_out_pending(ts_start) - participants = self.payday.get_participants(ts_start) + + participants = get_participants() expected_logging_call_args = [ ('Starting a new payday.'), ('Payday started at {}.'.format(ts_start)), + ('Prepared the DB.'), ('Zeroed out the pending column.'), - ('Fetched participants.'), ] expected_logging_call_args.reverse() for args, _ in log.call_args_list: @@ -526,8 +422,9 @@ def test_start_zero_out_and_get_participants(self, log): # run a second time, we should see it pick up the existing payday second_ts_start = self.payday.start() + self.payday.prepare(second_ts_start) self.payday.zero_out_pending(second_ts_start) - second_participants = self.payday.get_participants(second_ts_start) + second_participants = get_participants() assert ts_start == second_ts_start participants = list(participants) @@ -540,8 +437,8 @@ def test_start_zero_out_and_get_participants(self, log): expected_logging_call_args = [ ('Picking up with an existing payday.'), ('Payday started at {}.'.format(second_ts_start)), - ('Zeroed out the pending column.'), - ('Fetched participants.')] + ('Prepared the DB.'), + ('Zeroed out the pending column.')] expected_logging_call_args.reverse() for args, _ in log.call_args_list: assert args[0] == expected_logging_call_args.pop() @@ -717,17 +614,6 @@ def setUp(self): Harness.setUp(self) self.payday = Payday(self.db) - def test_get_participants_gets_participants(self): - a_team = self.make_participant('a_team', claimed_time='now', number='plural', balance=20) - a_team.add_member(self.make_participant('alice', claimed_time='now')) - a_team.add_member(self.make_participant('bob', claimed_time='now')) - - ts_start = self.payday.start() - - actual = [p.username for p in self.payday.get_participants(ts_start)] - expected = ['a_team', 'alice', 'bob'] - assert actual == expected - def test_pachinko_pachinkos(self): a_team = self.make_participant('a_team', claimed_time='now', number='plural', balance=20, \ pending=0) @@ -736,8 +622,8 @@ def test_pachinko_pachinkos(self): ts_start = self.payday.start() - participants = self.payday.genparticipants(ts_start, LOOP_PACHINKO) - self.payday.pachinko(ts_start, participants) + self.payday.prepare(ts_start) + self.payday.pachinko(ts_start) assert Participant.from_username('alice').pending == D('0.01') assert Participant.from_username('bob').pending == D('0.01') @@ -751,8 +637,8 @@ def test_pachinko_sees_current_take(self): ts_start = self.payday.start() - participants = self.payday.genparticipants(ts_start, LOOP_PACHINKO) - self.payday.pachinko(ts_start, participants) + self.payday.prepare(ts_start) + self.payday.pachinko(ts_start) assert Participant.from_username('alice').pending == D('1.00') @@ -766,8 +652,8 @@ def test_pachinko_ignores_take_set_after_payday_starts(self): ts_start = self.payday.start() a_team.set_take_for(alice, D('1.00'), alice) - participants = self.payday.genparticipants(ts_start, LOOP_PACHINKO) - self.payday.pachinko(ts_start, participants) + self.payday.prepare(ts_start) + self.payday.pachinko(ts_start) assert Participant.from_username('alice').pending == D('0.33') @@ -782,7 +668,7 @@ def test_pachinko_ignores_take_thats_already_been_processed(self): a_team.set_take_for(alice, D('1.00'), alice) for i in range(4): - participants = self.payday.genparticipants(ts_start, LOOP_PACHINKO) - self.payday.pachinko(ts_start, participants) + self.payday.prepare(ts_start) + self.payday.pachinko(ts_start) assert Participant.from_username('alice').pending == D('0.33') From ba6f4f7810dfe8b396fd122c18ccf2993392b033 Mon Sep 17 00:00:00 2001 From: Changaco Date: Sun, 18 May 2014 23:30:22 +0200 Subject: [PATCH 02/97] replace `get_tips_and_total()` and `get_takes()` --- bin/masspay.py | 2 +- gittip/models/_mixin_team.py | 63 ++++++----------------------- gittip/models/participant.py | 78 ++++-------------------------------- tests/py/test_close.py | 4 +- tests/py/test_teams.py | 4 +- www/%username/tips.json.spt | 2 +- 6 files changed, 25 insertions(+), 128 deletions(-) diff --git a/bin/masspay.py b/bin/masspay.py index dd039d9214..ce2ac38b86 100755 --- a/bin/masspay.py +++ b/bin/masspay.py @@ -136,7 +136,7 @@ def compute_input_csv(): print_rule(88) total_gross = 0 for participant in participants: - tips, total = participant.get_tips_and_total(for_payday=False) + total = participant.giving + participant.pledging amount = participant.balance - total if amount < 0.50: # Minimum payout of 50 cents. I think that otherwise PayPal upcharges to a penny. diff --git a/gittip/models/_mixin_team.py b/gittip/models/_mixin_team.py index 5a866b267f..d4cea3559b 100644 --- a/gittip/models/_mixin_team.py +++ b/gittip/models/_mixin_team.py @@ -29,7 +29,7 @@ def show_as_team(self, user): return False if user.ADMIN: return True - if not self.get_takes(): + if not self.get_current_takes(): if self == user.participant: return True return False @@ -39,7 +39,7 @@ def add_member(self, member): """Add a member to this team. """ assert self.IS_PLURAL - if len(self.get_takes()) == 149: + if len(self.get_current_takes()) == 149: raise MemberLimitReached if not member.is_claimed: raise StubParticipantAdded @@ -65,7 +65,7 @@ def member_of(self, team): """Given a Participant object, return a boolean. """ assert team.IS_PLURAL - for take in team.get_takes(): + for take in team.get_current_takes(): if take['member'] == self.username: return True return False @@ -180,56 +180,17 @@ def update_taking(self, old_takes, new_takes, cursor=None, member=None): if member and username == member.username: member.set_attributes(**r._asdict()) - def get_takes(self, for_payday=False, cursor=None): + def get_current_takes(self, cursor=None): """Return a list of member takes for a team. - - This is implemented parallel to Participant.get_tips_and_total. See - over there for an explanation of for_payday. - """ assert self.IS_PLURAL - - args = dict(team=self.username) - - if for_payday: - args['ts_start'] = for_payday - - # Get the takes for this team, as they were before ts_start, - # filtering out the ones we've already transferred (in case payday - # is interrupted and restarted). - - TAKES = """\ - - SELECT * FROM ( - SELECT DISTINCT ON (member) t.* - FROM takes t - JOIN participants p ON p.username = member - WHERE team=%(team)s - AND mtime < %(ts_start)s - AND p.is_suspicious IS NOT true - AND ( SELECT id - FROM transfers - WHERE tipper=t.team - AND tippee=t.member - AND context='take' - AND timestamp >= %(ts_start)s - ) IS NULL - ORDER BY member, mtime DESC - ) AS foo - ORDER BY ctime DESC - - """ - else: - TAKES = """\ - - SELECT member, amount, ctime, mtime - FROM current_takes - WHERE team=%(team)s - ORDER BY ctime DESC - - """ - - records = (cursor or self.db).all(TAKES, args) + TAKES = """ + SELECT member, amount, ctime, mtime + FROM current_takes + WHERE team=%(team)s + ORDER BY ctime DESC + """ + records = (cursor or self.db).all(TAKES, dict(team=self.username)) return [r._asdict() for r in records] def get_team_take(self, cursor=None): @@ -250,7 +211,7 @@ def compute_actual_takes(self, cursor=None): """Get the takes, compute the actual amounts, and return an OrderedDict. """ actual_takes = OrderedDict() - nominal_takes = self.get_takes(cursor=cursor) + nominal_takes = self.get_current_takes(cursor=cursor) nominal_takes.append(self.get_team_take(cursor=cursor)) budget = balance = self.balance + self.receiving for take in nominal_takes: diff --git a/gittip/models/participant.py b/gittip/models/participant.py index ae4d56adea..3060036381 100644 --- a/gittip/models/participant.py +++ b/gittip/models/participant.py @@ -846,55 +846,10 @@ def get_giving_for_profile(self): return tips, total, unclaimed_tips, unclaimed_total - def get_tips_and_total(self, for_payday=False): - """Given a participant id and a date, return a list and a Decimal. - - This function is used by the payday function. If for_payday is not - False it must be a date object. Originally we also used this function - to populate the profile page, but our requirements there changed while, - oddly, our requirements in payday *also* changed to match the old - requirements of the profile page. So this function keeps the for_payday - parameter after all. - + def get_current_tips(self): + """Get the tips this participant is currently sending to others. """ - - if for_payday: - - # For payday we want the oldest relationship to be paid first. - order_by = "ctime ASC" - - - # This is where it gets crash-proof. - # ================================== - # We need to account for the fact that we may have crashed during - # Payday and we're re-running that function. We only want to select - # tips that existed before Payday started, but haven't been - # processed as part of this Payday yet. - # - # It's a bug if the paydays subselect returns > 1 rows. - # - # XXX If we crash during Payday and we rerun it after a timezone - # change, will we get burned? How? - - ts_filter = """\ - - AND mtime < %s - AND ( SELECT id - FROM transfers - WHERE tipper=t.tipper - AND tippee=t.tippee - AND timestamp >= %s - ) IS NULL - - """ - args = (self.username, for_payday, for_payday) - else: - order_by = "amount DESC" - ts_filter = "" - args = (self.username,) - - TIPS = """\ - + TIPS = """ SELECT * FROM ( SELECT DISTINCT ON (tippee) amount @@ -903,34 +858,15 @@ def get_tips_and_total(self, for_payday=False): , p.claimed_time FROM tips t JOIN participants p ON p.username = t.tippee - WHERE tipper = %%s + WHERE tipper = %s AND p.is_suspicious IS NOT true - %s ORDER BY tippee , t.mtime DESC ) AS foo - ORDER BY %s + ORDER BY amount DESC , tippee - - """ % (ts_filter, order_by) # XXX, No injections here, right?! - tips = self.db.all(TIPS, args, back_as=dict) - - - # Compute the total. - # ================== - # For payday we only want to process payments to tippees who have - # themselves opted into Gittip. For the tipper's profile page we want - # to show the total amount they've pledged (so they're not surprised - # when someone *does* start accepting tips and all of a sudden they're - # hit with bigger charges. - - if for_payday: - to_total = [t for t in tips if t['claimed_time'] is not None] - else: - to_total = tips - total = sum([t['amount'] for t in to_total], Decimal('0.00')) - - return tips, total + """ + return self.db.all(TIPS, (self.username,), back_as=dict) def get_og_title(self): diff --git a/tests/py/test_close.py b/tests/py/test_close.py index 0091c4c51c..a5c7edbdbc 100644 --- a/tests/py/test_close.py +++ b/tests/py/test_close.py @@ -331,12 +331,12 @@ def test_cpi_clears_teams(self): bob = self.make_participant('bob', claimed_time='now') team.add_member(bob) - assert len(team.get_takes()) == 2 # sanity check + assert len(team.get_current_takes()) == 2 # sanity check with self.db.get_cursor() as cursor: alice.clear_personal_information(cursor) - assert len(team.get_takes()) == 1 + assert len(team.get_current_takes()) == 1 # uic = update_is_closed diff --git a/tests/py/test_teams.py b/tests/py/test_teams.py index 4b97b08e12..39c275db40 100644 --- a/tests/py/test_teams.py +++ b/tests/py/test_teams.py @@ -88,6 +88,6 @@ def test_remove_all_members(self): bob = self.make_participant('bob', claimed_time='now') self.team.add_member(bob) - assert len(self.team.get_takes()) == 2 # sanity check + assert len(self.team.get_current_takes()) == 2 # sanity check self.team.remove_all_members() - assert len(self.team.get_takes()) == 0 + assert len(self.team.get_current_takes()) == 0 diff --git a/www/%username/tips.json.spt b/www/%username/tips.json.spt index 9e1935b748..789949f727 100644 --- a/www/%username/tips.json.spt +++ b/www/%username/tips.json.spt @@ -29,7 +29,7 @@ if POST: out.append(one) if qs.get('also_prune', 'false').lower() in ('true', '1', 'yes'): - old_tips, __ignored = participant.get_tips_and_total() + old_tips = participant.get_current_tips() for tip in old_tips: if tip['tippee'] not in seen: participant.set_tip_to(tip['tippee'], '0.00') From 466eb463d8dac78a6864f9564561a9e3800da774 Mon Sep 17 00:00:00 2001 From: Changaco Date: Sat, 21 Jun 2014 17:57:42 +0200 Subject: [PATCH 03/97] remove unused arguments --- gittip/billing/payday.py | 20 +++++++++----------- tests/py/test_billing_payday.py | 17 +++++++---------- 2 files changed, 16 insertions(+), 21 deletions(-) diff --git a/gittip/billing/payday.py b/gittip/billing/payday.py index c249ee8230..cc439c4592 100644 --- a/gittip/billing/payday.py +++ b/gittip/billing/payday.py @@ -115,9 +115,9 @@ def run(self): self.prepare(ts_start) self.zero_out_pending(ts_start) - self.payin(ts_start) + self.payin() self.move_pending_to_balance_for_teams() - self.pachinko(ts_start) + self.pachinko() self.clear_pending_to_balance() self.payout() self.update_stats(ts_start) @@ -263,8 +263,8 @@ def zero_out_pending(self, ts_start): return None - def payin(self, ts_start): - """Given a datetime, do the payin side of Payday. + def payin(self): + """Do the payin side of Payday. """ i = 0 log("Starting payin loop.") @@ -274,11 +274,11 @@ def payin(self, ts_start): for i, participant in enumerate(participants, start=1): if i % 100 == 0: log("Payin done for %d participants." % i) - self.charge_and_or_transfer(ts_start, participant) + self.charge_and_or_transfer(participant) log("Did payin for %d participants." % i) - def pachinko(self, ts_start): + def pachinko(self): i = 0 participants = self.db.all(""" SELECT * FROM pay_participants WHERE number = 'plural' @@ -297,10 +297,8 @@ def tip(tippee, amount): tip['tipper'] = participant.username tip['tippee'] = tippee tip['amount'] = amount - tip['claimed_time'] = ts_start self.tip( participant , tip - , ts_start , pachinko=True ) @@ -334,7 +332,7 @@ def payout(self): log("Did payout for %d participants." % i) - def charge_and_or_transfer(self, ts_start, participant): + def charge_and_or_transfer(self, participant): """Given one participant record, pay their day. Charge each participants' credit card if needed before transfering @@ -359,7 +357,7 @@ def charge_and_or_transfer(self, ts_start, participant): nsuccessful_tips = 0 for tip in tips: - result = self.tip(participant, tip, ts_start) + result = self.tip(participant, tip) if result >= 0: nsuccessful_tips += result else: @@ -507,7 +505,7 @@ def end(self): # Move money between Gittip participants. # ======================================= - def tip(self, participant, tip, ts_start, pachinko=False): + def tip(self, participant, tip, pachinko=False): """Given dict, dict, and datetime, log and return int. Return values: diff --git a/tests/py/test_billing_payday.py b/tests/py/test_billing_payday.py index 457c7e838b..99e96b3b7c 100644 --- a/tests/py/test_billing_payday.py +++ b/tests/py/test_billing_payday.py @@ -365,9 +365,8 @@ def test_tip(self, log, transfer): , 'tippee': 'janet' , 'claimed_time': utcnow() } - ts_start = utcnow() - result = self.payday.tip(self.janet, tip, ts_start) + result = self.payday.tip(self.janet, tip) assert result == 1 result = transfer.called_with('janet', tip['tippee'], tip['amount']) assert result @@ -380,17 +379,15 @@ def test_tip(self, log, transfer): # XXX: We should have constants to compare the values to # invalid amount tip['amount'] = invalid_amount - result = self.payday.tip(self.janet, tip, ts_start) + result = self.payday.tip(self.janet, tip) assert result == 0 tip['amount'] = amount - ts_start = utcnow() - # XXX: We should have constants to compare the values to # transfer failed transfer.return_value = False - result = self.payday.tip(self.janet, tip, ts_start) + result = self.payday.tip(self.janet, tip) assert result == -1 @mock.patch('gittip.billing.payday.log') @@ -623,7 +620,7 @@ def test_pachinko_pachinkos(self): ts_start = self.payday.start() self.payday.prepare(ts_start) - self.payday.pachinko(ts_start) + self.payday.pachinko() assert Participant.from_username('alice').pending == D('0.01') assert Participant.from_username('bob').pending == D('0.01') @@ -638,7 +635,7 @@ def test_pachinko_sees_current_take(self): ts_start = self.payday.start() self.payday.prepare(ts_start) - self.payday.pachinko(ts_start) + self.payday.pachinko() assert Participant.from_username('alice').pending == D('1.00') @@ -653,7 +650,7 @@ def test_pachinko_ignores_take_set_after_payday_starts(self): a_team.set_take_for(alice, D('1.00'), alice) self.payday.prepare(ts_start) - self.payday.pachinko(ts_start) + self.payday.pachinko() assert Participant.from_username('alice').pending == D('0.33') @@ -669,6 +666,6 @@ def test_pachinko_ignores_take_thats_already_been_processed(self): for i in range(4): self.payday.prepare(ts_start) - self.payday.pachinko(ts_start) + self.payday.pachinko() assert Participant.from_username('alice').pending == D('0.33') From d2d2009561a060abc22374949ab221bd8a95c841 Mon Sep 17 00:00:00 2001 From: Changaco Date: Fri, 20 Jun 2014 19:40:31 +0200 Subject: [PATCH 04/97] factor record_charge and record_credit --- gittip/billing/payday.py | 116 ++++++++++---------------------- tests/py/test_billing_payday.py | 36 +++++----- 2 files changed, 54 insertions(+), 98 deletions(-) diff --git a/gittip/billing/payday.py b/gittip/billing/payday.py index cc439c4592..959299ef50 100644 --- a/gittip/billing/payday.py +++ b/gittip/billing/payday.py @@ -654,14 +654,13 @@ def charge(self, participant, amount): ) charge_amount, fee, error = things + if error: + self.mark_charge_failed() + amount = charge_amount - fee # account for possible rounding under # charge_on_* - self.record_charge( amount - , fee - , error - , username - ) + self.record_exchange('bill', amount, fee, error, participant) def ach_credit(self, participant, total, minimum_credit=MINIMUM_CREDIT): @@ -733,8 +732,9 @@ def ach_credit(self, participant, total, minimum_credit=MINIMUM_CREDIT): if error: log(msg + "failed: %s" % error) + self.mark_ach_failed() - self.record_credit(credit_amount, fee, error, participant) + self.record_exchange('ach', -credit_amount, fee, error, participant) def charge_on_balanced(self, username, balanced_customer_href, amount): @@ -775,7 +775,7 @@ def _prep_hit(self, unrounded): upcharged Decimal dollar equivalent to `cents'. fee Decimal dollar amount of the fee portion of `upcharged'. - The latter two end up in the db in a couple places via record_charge. + The latter two end up in the db in a couple places via record_exchange. """ also_log = '' @@ -796,9 +796,18 @@ def _prep_hit(self, unrounded): # Record-keeping. # =============== - def record_charge(self, amount, fee, error, username): + def record_exchange(self, kind, amount, fee, error, participant): """Given a Bunch of Stuff, return None. + Records in the exchanges table have these characteristics: + + amount It's negative for credits (representing an outflow from + Gittip to you) and positive for charges. + The sign is how we differentiate the two in, e.g., the + history page. + + fee The payment processor's fee. It's always positive. + This function takes the result of an API call to a payment processor and records the result in our db. If the power goes out at this point then Postgres will be out of sync with the payment processor. We'll @@ -818,14 +827,12 @@ def record_charge(self, amount, fee, error, username): """ + username = participant.username with self.db.get_cursor() as cursor: if error: - last_bill_result = error - amount = Decimal('0.00') - self.mark_charge_failed(cursor) + amount = fee = Decimal('0.00') else: - last_bill_result = '' EXCHANGE = """\ INSERT INTO exchanges @@ -835,77 +842,26 @@ def record_charge(self, amount, fee, error, username): """ cursor.execute(EXCHANGE, (amount, fee, username)) - # Update the participant's balance. # ================================= - # Credit card charges go immediately to balance, not to pending. RESULT = """\ - UPDATE participants - SET last_bill_result=%s - , balance=(balance + %s) - WHERE username=%s - - """ - cursor.execute(RESULT, (last_bill_result, amount, username)) - + UPDATE participants + SET last_{0}_result=%s + , balance=(balance + %s) + WHERE username=%s + RETURNING balance - def record_credit(self, amount, fee, error, participant): - """Given a Bunch of Stuff, return None. - - Records in the exchanges table for credits have these characteristics: - - amount It's negative, representing an outflow from Gittip to you. - This is oppositive of charges, where amount is positive. - The sign is how we differentiate the two in, e.g., the - history page. - - fee It's positive, just like with charges. - - """ - username = participant.username - credit = -amount # From Gittip's POV this is money flowing out of the - # system. - - with self.db.get_cursor() as cursor: - - if error: - last_ach_result = error - credit = fee = Decimal('0.00') # ensures balance won't change - self.mark_ach_failed(cursor) - else: - last_ach_result = '' - EXCHANGE = """\ - - INSERT INTO exchanges - (amount, fee, participant) - VALUES (%s, %s, %s) - - """ - cursor.execute(EXCHANGE, (credit, fee, username)) - - - # Update the participant's balance. - # ================================= - - RESULT = """\ - - UPDATE participants - SET last_ach_result=%s - , balance=(balance + %s) - WHERE username=%s - RETURNING balance - - """ - balance = cursor.one(RESULT, ( last_ach_result - , credit - fee # -10.00 - 0.30 = -10.30 - , username - )) + """.format(kind) + if kind == 'ach': + amount -= fee + balance = cursor.one(RESULT, (error or '', amount, username)) if balance < 0: raise NegativeBalance - participant.set_attributes(balance=balance) + if hasattr(participant, 'set_attributes'): + participant.set_attributes(balance=balance) def record_transfer(self, cursor, tipper, tippee, amount, context): @@ -929,21 +885,19 @@ def mark_missing_funding(self): """, default=NoPayday) - def mark_charge_failed(self, cursor): - STATS = """\ + def mark_charge_failed(self): + self.db.one("""\ UPDATE paydays SET ncc_failing = ncc_failing + 1 WHERE ts_end='1970-01-01T00:00:00+00'::timestamptz RETURNING id - """ - cursor.execute(STATS) - assert cursor.fetchone() is not None + """, default=NoPayday) - def mark_ach_failed(self, cursor): - cursor.one("""\ + def mark_ach_failed(self): + self.db.one("""\ UPDATE paydays SET nach_failing = nach_failing + 1 diff --git a/tests/py/test_billing_payday.py b/tests/py/test_billing_payday.py index 99e96b3b7c..1556864829 100644 --- a/tests/py/test_billing_payday.py +++ b/tests/py/test_billing_payday.py @@ -234,8 +234,7 @@ def test_mark_charge_failed(self): before = self.fetch_payday() fail_count = before['ncc_failing'] - with self.db.get_cursor() as cursor: - self.payday.mark_charge_failed(cursor) + self.payday.mark_charge_failed() after = self.fetch_payday() assert after['ncc_failing'] == fail_count + 1 @@ -574,33 +573,36 @@ def test_record_transfer_invalid_participant(self): , 'tip' ) - def test_record_credit_updates_balance(self): + def test_record_exchange_updates_balance(self): alice = Participant.from_username('alice') - self.payday.record_credit( amount=D("-1.00") - , fee=D("0.41") - , error="" - , participant=alice - ) + self.payday.record_exchange( 'bill' + , amount=D("0.59") + , fee=D("0.41") + , error="" + , participant=alice + ) alice = Participant.from_username('alice') assert alice.balance == D("0.59") - def test_record_credit_fails_if_negative_balance(self): + def test_record_exchange_fails_if_negative_balance(self): alice = Participant.from_username('alice') pytest.raises( NegativeBalance - , self.payday.record_credit - , amount=D("10.00") + , self.payday.record_exchange + , 'ach' + , amount=D("-10.00") , fee=D("0.41") , error="" , participant=alice ) - def test_record_credit_doesnt_update_balance_if_error(self): + def test_record_exchange_doesnt_update_balance_if_error(self): alice = Participant.from_username('alice') - self.payday.record_credit( amount=D("-1.00") - , fee=D("0.41") - , error="SOME ERROR" - , participant=alice - ) + self.payday.record_exchange( 'bill' + , amount=D("1.00") + , fee=D("0.41") + , error="SOME ERROR" + , participant=alice + ) alice = Participant.from_username('alice') assert alice.balance == D("0.00") From e092267152d3e812cd9aefb515bc2cb706fe7847 Mon Sep 17 00:00:00 2001 From: Changaco Date: Sat, 21 Jun 2014 17:37:02 +0200 Subject: [PATCH 05/97] remove monkey-patching --- gittip/billing/payday.py | 6 ++++-- gittip/models/participant.py | 3 --- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/gittip/billing/payday.py b/gittip/billing/payday.py index 959299ef50..203f2869f2 100644 --- a/gittip/billing/payday.py +++ b/gittip/billing/payday.py @@ -328,7 +328,9 @@ def payout(self): if i % 100 == 0: log("Payout done for %d participants." % i) withhold = participant.giving + participant.pledging - self.ach_credit(participant, withhold) + error = self.ach_credit(participant, withhold) + if error: + self.mark_ach_failed() log("Did payout for %d participants." % i) @@ -732,9 +734,9 @@ def ach_credit(self, participant, total, minimum_credit=MINIMUM_CREDIT): if error: log(msg + "failed: %s" % error) - self.mark_ach_failed() self.record_exchange('ach', -credit_amount, fee, error, participant) + return error def charge_on_balanced(self, username, balanced_customer_href, amount): diff --git a/gittip/models/participant.py b/gittip/models/participant.py index 3060036381..d73f9e38bc 100644 --- a/gittip/models/participant.py +++ b/gittip/models/participant.py @@ -288,9 +288,6 @@ def withdraw_balance_to_bank_account(self, cursor): hack = Payday(self.db) # Our payout code is on the Payday object. Rather than # refactor right now, let's just use it from there. - # Monkey-patch a couple methods, coopting them for callbacks, essentially. - hack.mark_ach_failed = lambda cursor: None - hack.ach_credit( self , Decimal('0.00') # don't withhold anything , Decimal('0.00') # send it all From 645c41a956c79e0140243a694fc0a28199160819 Mon Sep 17 00:00:00 2001 From: Changaco Date: Sat, 21 Jun 2014 22:57:11 +0200 Subject: [PATCH 06/97] rename argument --- gittip/billing/payday.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/gittip/billing/payday.py b/gittip/billing/payday.py index 203f2869f2..a4a55c044b 100644 --- a/gittip/billing/payday.py +++ b/gittip/billing/payday.py @@ -665,7 +665,7 @@ def charge(self, participant, amount): self.record_exchange('bill', amount, fee, error, participant) - def ach_credit(self, participant, total, minimum_credit=MINIMUM_CREDIT): + def ach_credit(self, participant, withhold, minimum_credit=MINIMUM_CREDIT): # Compute the amount to credit them. # ================================== @@ -674,7 +674,7 @@ def ach_credit(self, participant, total, minimum_credit=MINIMUM_CREDIT): balance = participant.balance assert balance is not None, balance # sanity check - amount = balance - total + amount = balance - withhold # Do some last-minute checks. # =========================== @@ -684,9 +684,9 @@ def ach_credit(self, participant, total, minimum_credit=MINIMUM_CREDIT): if amount < minimum_credit: also_log = "" - if total > 0: + if withhold > 0: also_log = " ($%s balance - $%s in obligations)" - also_log %= (balance, total) + 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. @@ -701,9 +701,9 @@ def ach_credit(self, participant, total, minimum_credit=MINIMUM_CREDIT): credit_amount, fee = skim_credit(amount) cents = credit_amount * 100 - if total > 0: + if withhold > 0: also_log = "$%s balance - $%s in obligations" - also_log %= (balance, total) + also_log %= (balance, withhold) else: also_log = "$%s" % amount msg = "Crediting %s %d cents (%s - $%s fee = $%s) on Balanced ... " From 60ab7241731549b5d2cda7b47ea8aa6071429a63 Mon Sep 17 00:00:00 2001 From: Changaco Date: Sat, 21 Jun 2014 23:03:18 +0200 Subject: [PATCH 07/97] move exceptions from withdraw_balance_to_bank_account to ach_credit --- gittip/billing/payday.py | 30 ++++++++++++++++++------------ gittip/exceptions.py | 3 +++ gittip/models/participant.py | 8 -------- tests/py/test_close.py | 7 ++++--- 4 files changed, 25 insertions(+), 23 deletions(-) diff --git a/gittip/billing/payday.py b/gittip/billing/payday.py index a4a55c044b..9fad24caec 100644 --- a/gittip/billing/payday.py +++ b/gittip/billing/payday.py @@ -25,7 +25,7 @@ import aspen.utils from aspen import log from aspen.utils import typecheck -from gittip.exceptions import NegativeBalance +from gittip.exceptions import NegativeBalance, NoBalancedCustomerHref, NotWhitelisted from psycopg2 import IntegrityError @@ -328,9 +328,15 @@ def payout(self): if i % 100 == 0: log("Payout done for %d participants." % i) withhold = participant.giving + participant.pledging - error = self.ach_credit(participant, withhold) - if error: - self.mark_ach_failed() + try: + error = self.ach_credit(participant, withhold) + if error: + self.mark_ach_failed() + except NoBalancedCustomerHref: + continue + except NotWhitelisted: + if participant.is_suspicious is None: + log("UNREVIEWED: %s" % participant.username) log("Did payout for %d participants." % i) @@ -691,8 +697,14 @@ def ach_credit(self, participant, withhold, minimum_credit=MINIMUM_CREDIT): % (minimum_credit, participant.username, amount, also_log)) return # Participant owed too little. - if not is_whitelisted(participant): - return # Participant not trusted. + if not participant.is_whitelisted: + raise NotWhitelisted # Participant not trusted. + + balanced_customer_href = participant.balanced_customer_href + if balanced_customer_href is None: + log("%s has no balanced_customer_href." + % participant.username) + raise NoBalancedCustomerHref # not in Balanced # Do final calculations. @@ -714,12 +726,6 @@ def ach_credit(self, participant, withhold, minimum_credit=MINIMUM_CREDIT): # =========================== try: - balanced_customer_href = participant.balanced_customer_href - if balanced_customer_href is None: - log("%s has no balanced_customer_href." - % participant.username) - return # not in Balanced - customer = balanced.Customer.fetch(balanced_customer_href) customer.bank_accounts.one()\ .credit(amount=cents, diff --git a/gittip/exceptions.py b/gittip/exceptions.py index a75fb6fd1e..9e1bd1d340 100644 --- a/gittip/exceptions.py +++ b/gittip/exceptions.py @@ -44,3 +44,6 @@ class FailedToReserveUsername(Exception): pass class NegativeBalance(Exception): def __str__(self): return "Negative balance not allowed in this context." + +class NotWhitelisted(Exception): pass +class NoBalancedCustomerHref(Exception): pass diff --git a/gittip/models/participant.py b/gittip/models/participant.py index d73f9e38bc..0d371b22c3 100644 --- a/gittip/models/participant.py +++ b/gittip/models/participant.py @@ -275,15 +275,7 @@ def close(self, disbursement_strategy): self.update_is_closed(True, cursor) - class NotWhitelisted(Exception): pass - class NoBalancedCustomerHref(Exception): pass - def withdraw_balance_to_bank_account(self, cursor): - if self.is_suspicious in (True, None): - raise self.NotWhitelisted - if self.balanced_customer_href is None: - raise self.NoBalancedCustomerHref - from gittip.billing.payday import Payday hack = Payday(self.db) # Our payout code is on the Payday object. Rather than # refactor right now, let's just use it from there. diff --git a/tests/py/test_close.py b/tests/py/test_close.py index a5c7edbdbc..45352c44b7 100644 --- a/tests/py/test_close.py +++ b/tests/py/test_close.py @@ -6,6 +6,7 @@ import balanced import pytest from gittip.billing.payday import Payday +from gittip.exceptions import NoBalancedCustomerHref, NotWhitelisted from gittip.models.community import Community from gittip.models.participant import Participant from gittip.testing import Harness @@ -85,19 +86,19 @@ def test_wbtba_withdraws_balance_to_bank_account(self): def test_wbtba_raises_NoBalancedCustomerHref_if_no_balanced_customer_href(self): alice = self.make_participant('alice', balance=D('10.00'), is_suspicious=False) with self.db.get_cursor() as cursor: - with pytest.raises(alice.NoBalancedCustomerHref): + with pytest.raises(NoBalancedCustomerHref): alice.withdraw_balance_to_bank_account(cursor) def test_wbtba_raises_NotWhitelisted_if_not_whitelisted(self): alice = self.make_participant('alice', balance=D('10.00')) with self.db.get_cursor() as cursor: - with pytest.raises(alice.NotWhitelisted): + with pytest.raises(NotWhitelisted): alice.withdraw_balance_to_bank_account(cursor) def test_wbtba_raises_NotWhitelisted_if_blacklisted(self): alice = self.make_participant('alice', balance=D('10.00'), is_suspicious=True) with self.db.get_cursor() as cursor: - with pytest.raises(alice.NotWhitelisted): + with pytest.raises(NotWhitelisted): alice.withdraw_balance_to_bank_account(cursor) From fd5176cd4bf4dbcc5d1801d4f0c2ecd528a75be6 Mon Sep 17 00:00:00 2001 From: Changaco Date: Sun, 22 Jun 2014 10:52:21 +0200 Subject: [PATCH 08/97] use the same exceptions in charge() --- gittip/billing/payday.py | 35 ++++++++++++----------------------- 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/gittip/billing/payday.py b/gittip/billing/payday.py index 9fad24caec..1582eb6da5 100644 --- a/gittip/billing/payday.py +++ b/gittip/billing/payday.py @@ -63,21 +63,6 @@ def skim_credit(amount): assert upcharge(MINIMUM_CHARGE) == (Decimal('10.00'), Decimal('0.59')) -def is_whitelisted(participant): - """Given a dict, return bool, possibly logging. - - We only perform credit card charges and bank deposits for whitelisted - participants. We don't even include is_suspicious participants in the - initial SELECT, so we should never see one here. - - """ - assert participant.is_suspicious is not True, participant.username - if participant.is_suspicious is None: - log("UNREVIEWED: %s" % participant.username) - return False - return True - - class NoPayday(Exception): def __str__(self): return "No payday found where one was expected." @@ -357,7 +342,15 @@ def charge_and_or_transfer(self, participant): # at least *some* tips. The charge method will have set # last_bill_result to a non-empty string if the card did fail. - self.charge(participant, short) + try: + error = self.charge(participant, short) + if error: + self.mark_charge_failed() + except NoBalancedCustomerHref: + self.mark_missing_funding() + except NotWhitelisted: + if participant.is_suspicious is None: + log("UNREVIEWED: %s" % participant.username) tips = self.db.all(""" SELECT * FROM pay_tips WHERE tipper = %s @@ -646,11 +639,10 @@ def charge(self, participant, amount): # ================================ if balanced_customer_href is None: - self.mark_missing_funding() - return # Participant has no funding source. + raise NoBalancedCustomerHref # Participant has no funding source. - if not is_whitelisted(participant): - return # Participant not trusted. + if participant.is_suspicious is not False: + raise NotWhitelisted # Participant not trusted. # Go to Balanced. @@ -662,9 +654,6 @@ def charge(self, participant, amount): ) charge_amount, fee, error = things - if error: - self.mark_charge_failed() - amount = charge_amount - fee # account for possible rounding under # charge_on_* From 59a65b4e5bc1c5bde6d8022a9d8f328259bd1e58 Mon Sep 17 00:00:00 2001 From: Changaco Date: Sun, 22 Jun 2014 12:48:17 +0200 Subject: [PATCH 09/97] create new module gittip.billing.exchanges --- gittip/billing/exchanges.py | 286 ++++++++++++++++++++++++++++++++++ gittip/billing/payday.py | 287 +---------------------------------- gittip/models/participant.py | 14 +- 3 files changed, 296 insertions(+), 291 deletions(-) create mode 100644 gittip/billing/exchanges.py diff --git a/gittip/billing/exchanges.py b/gittip/billing/exchanges.py new file mode 100644 index 0000000000..1ad4d6c99d --- /dev/null +++ b/gittip/billing/exchanges.py @@ -0,0 +1,286 @@ +"""Functions for moving money between Gittip and the outside world. +""" +from __future__ import unicode_literals + +from decimal import Decimal, ROUND_UP +import sys + +import balanced + +from aspen import log +from aspen.utils import typecheck +from gittip.exceptions import NegativeBalance, NoBalancedCustomerHref, NotWhitelisted + + +# Balanced has a $0.50 minimum. We go even higher to avoid onerous +# per-transaction fees. See: +# https://github.com/gittip/www.gittip.com/issues/167 + +MINIMUM_CHARGE = Decimal("9.41") +MINIMUM_CREDIT = Decimal("10.00") + +FEE_CHARGE = ( Decimal("0.30") # $0.30 + , Decimal("0.029") # 2.9% + ) +FEE_CREDIT = Decimal("0.00") # Balanced doesn't actually charge us for this, + # because we were in the door early enough. + + +def upcharge(amount): + """Given an amount, return a higher amount and the difference. + """ + typecheck(amount, Decimal) + charge_amount = (amount + FEE_CHARGE[0]) / (1 - FEE_CHARGE[1]) + charge_amount = charge_amount.quantize(FEE_CHARGE[0], rounding=ROUND_UP) + return charge_amount, charge_amount - amount + +assert upcharge(MINIMUM_CHARGE) == (Decimal('10.00'), Decimal('0.59')) + + +def skim_credit(amount): + """Given an amount, return a lower amount and the difference. + """ + typecheck(amount, Decimal) + return amount - FEE_CREDIT, FEE_CREDIT + + +def ach_credit(db, participant, withhold, minimum_credit=MINIMUM_CREDIT): + + # Compute the amount to credit them. + # ================================== + # Leave money in Gittip 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. + + balanced_customer_href = participant.balanced_customer_href + if balanced_customer_href is None: + log("%s has no balanced_customer_href." + % participant.username) + raise NoBalancedCustomerHref # not in Balanced + + + # 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. + # =========================== + + try: + customer = balanced.Customer.fetch(balanced_customer_href) + customer.bank_accounts.one()\ + .credit(amount=cents, + description=participant.username) + + log(msg + "succeeded.") + error = "" + except balanced.exc.HTTPError as err: + error = err.message.message + except: + error = repr(sys.exc_info()[1]) + + if error: + log(msg + "failed: %s" % error) + + record_exchange(db, 'ach', -credit_amount, fee, error, participant) + return error + + +def charge(db, participant, amount): + """Charge the participants credit card. + + This is the only place where we actually charge credit cards. Amount + should be the nominal amount. We'll compute Gittip's fee below this + function and add it to amount to end up with charge_amount. + + """ + typecheck(amount, Decimal) + + username = participant.username + balanced_customer_href = participant.balanced_customer_href + + typecheck( username, unicode + , balanced_customer_href, (unicode, None) + ) + + + # Perform some last-minute checks. + # ================================ + + if balanced_customer_href is None: + raise NoBalancedCustomerHref # Participant has no funding source. + + if participant.is_suspicious is not False: + raise NotWhitelisted # Participant not trusted. + + + # Go to Balanced. + # =============== + + things = charge_on_balanced( username + , balanced_customer_href + , amount + ) + charge_amount, fee, error = things + + amount = charge_amount - fee # account for possible rounding under + # charge_on_* + + record_exchange(db, 'bill', amount, fee, error, participant) + return error + + +def charge_on_balanced(username, balanced_customer_href, amount): + """We have a purported balanced_customer_href. Try to use it. + """ + typecheck( username, unicode + , balanced_customer_href, unicode + , amount, Decimal + ) + + cents, msg, charge_amount, fee = _prep_hit(amount) + msg = msg % (username, "Balanced") + + try: + customer = balanced.Customer.fetch(balanced_customer_href) + customer.cards.one().debit(amount=cents, description=username) + log(msg + "succeeded.") + error = "" + except balanced.exc.HTTPError as err: + error = err.message.message + except: + error = repr(sys.exc_info()[1]) + + if error: + log(msg + "failed: %s" % error) + + return charge_amount, fee, error + + +def _prep_hit(unrounded): + """Takes an amount in dollars. Returns cents, etc. + + cents This is passed to the payment processor charge API. This is + the value that is actually charged to the participant. It's + an int. + msg A log message with a couple %s to be filled in by the + caller. + upcharged Decimal dollar equivalent to `cents'. + fee Decimal dollar amount of the fee portion of `upcharged'. + + The latter two end up in the db in a couple places via record_exchange. + + """ + also_log = '' + rounded = unrounded + if unrounded < MINIMUM_CHARGE: + rounded = MINIMUM_CHARGE # per github/#167 + also_log = ' [rounded up from $%s]' % unrounded + + upcharged, fee = upcharge(rounded) + cents = int(upcharged * 100) + + msg = "Charging %%s %d cents ($%s%s + $%s fee = $%s) on %%s ... " + msg %= cents, rounded, also_log, fee, upcharged + + return cents, msg, upcharged, fee + + +def record_exchange(db, kind, amount, fee, error, participant): + """Given a Bunch of Stuff, return None. + + Records in the exchanges table have these characteristics: + + amount It's negative for credits (representing an outflow from + Gittip to you) and positive for charges. + The sign is how we differentiate the two in, e.g., the + history page. + + fee The payment processor's fee. It's always positive. + + This function takes the result of an API call to a payment processor + and records the result in our db. If the power goes out at this point + then Postgres will be out of sync with the payment processor. We'll + have to resolve that manually be reviewing the transaction log at the + processor and modifying Postgres accordingly. + + For Balanced, this could be automated by generating an ID locally and + commiting that to the db and then passing that through in the meta + field.* Then syncing would be a case of simply:: + + for payment in unresolved_payments: + payment_in_balanced = balanced.Transaction.query.filter( + **{'meta.unique_id': 'value'}).one() + payment.transaction_uri = payment_in_balanced.uri + + * https://www.balancedpayments.com/docs/meta + + """ + + username = participant.username + with db.get_cursor() as cursor: + + if error: + amount = fee = Decimal('0.00') + else: + EXCHANGE = """\ + + INSERT INTO exchanges + (amount, fee, participant) + VALUES (%s, %s, %s) + + """ + cursor.execute(EXCHANGE, (amount, fee, username)) + + # Update the participant's balance. + # ================================= + + RESULT = """\ + + UPDATE participants + SET last_{0}_result=%s + , balance=(balance + %s) + WHERE username=%s + RETURNING balance + + """.format(kind) + if kind == 'ach': + amount -= fee + balance = cursor.one(RESULT, (error or '', amount, username)) + if balance < 0: + raise NegativeBalance + + if hasattr(participant, 'set_attributes'): + participant.set_attributes(balance=balance) diff --git a/gittip/billing/payday.py b/gittip/billing/payday.py index 1582eb6da5..14fea554c2 100644 --- a/gittip/billing/payday.py +++ b/gittip/billing/payday.py @@ -18,51 +18,16 @@ """ from __future__ import unicode_literals -import sys -from decimal import Decimal, ROUND_UP +from decimal import Decimal -import balanced import aspen.utils from aspen import log from aspen.utils import typecheck +from gittip.billing.exchanges import ach_credit, charge from gittip.exceptions import NegativeBalance, NoBalancedCustomerHref, NotWhitelisted from psycopg2 import IntegrityError -# Set fees and minimums. -# ====================== -# Balanced has a $0.50 minimum. We go even higher to avoid onerous -# per-transaction fees. See: -# https://github.com/gittip/www.gittip.com/issues/167 XXX I should maybe -# compute this using *ahem* math. - -FEE_CHARGE = ( Decimal("0.30") # $0.30 - , Decimal("0.029") # 2.9% - ) -FEE_CREDIT = Decimal("0.00") # Balanced doesn't actually charge us for this, - # because we were in the door early enough. - -MINIMUM_CHARGE = Decimal("9.41") -MINIMUM_CREDIT = Decimal("10.00") - - -def upcharge(amount): - """Given an amount, return a higher amount and the difference. - """ - typecheck(amount, Decimal) - charge_amount = (amount + FEE_CHARGE[0]) / (1 - FEE_CHARGE[1]) - charge_amount = charge_amount.quantize(FEE_CHARGE[0], rounding=ROUND_UP) - return charge_amount, charge_amount - amount - -def skim_credit(amount): - """Given an amount, return a lower amount and the difference. - """ - typecheck(amount, Decimal) - return amount - FEE_CREDIT, FEE_CREDIT - -assert upcharge(MINIMUM_CHARGE) == (Decimal('10.00'), Decimal('0.59')) - - class NoPayday(Exception): def __str__(self): return "No payday found where one was expected." @@ -314,7 +279,7 @@ def payout(self): log("Payout done for %d participants." % i) withhold = participant.giving + participant.pledging try: - error = self.ach_credit(participant, withhold) + error = ach_credit(self.db, participant, withhold) if error: self.mark_ach_failed() except NoBalancedCustomerHref: @@ -343,7 +308,7 @@ def charge_and_or_transfer(self, participant): # last_bill_result to a non-empty string if the card did fail. try: - error = self.charge(participant, short) + error = charge(self.db, participant, short) if error: self.mark_charge_failed() except NoBalancedCustomerHref: @@ -614,253 +579,9 @@ def credit_participant(self, cursor, participant, amount): assert rec is not None, (participant, amount) # sanity check - # Move money between Gittip and the outside world. - # ================================================ - - def charge(self, participant, amount): - """Given dict and Decimal, return None. - - This is the only place where we actually charge credit cards. Amount - should be the nominal amount. We'll compute Gittip's fee below this - function and add it to amount to end up with charge_amount. - - """ - typecheck(amount, Decimal) - - username = participant.username - balanced_customer_href = participant.balanced_customer_href - - typecheck( username, unicode - , balanced_customer_href, (unicode, None) - ) - - - # Perform some last-minute checks. - # ================================ - - if balanced_customer_href is None: - raise NoBalancedCustomerHref # Participant has no funding source. - - if participant.is_suspicious is not False: - raise NotWhitelisted # Participant not trusted. - - - # Go to Balanced. - # =============== - - things = self.charge_on_balanced( username - , balanced_customer_href - , amount - ) - charge_amount, fee, error = things - - amount = charge_amount - fee # account for possible rounding under - # charge_on_* - - self.record_exchange('bill', amount, fee, error, participant) - - - def ach_credit(self, participant, withhold, minimum_credit=MINIMUM_CREDIT): - - # Compute the amount to credit them. - # ================================== - # Leave money in Gittip to cover their obligations next week (as these - # currently stand). Also reduce the amount by our service fee. - - 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. - - balanced_customer_href = participant.balanced_customer_href - if balanced_customer_href is None: - log("%s has no balanced_customer_href." - % participant.username) - raise NoBalancedCustomerHref # not in Balanced - - - # 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. - # =========================== - - try: - customer = balanced.Customer.fetch(balanced_customer_href) - customer.bank_accounts.one()\ - .credit(amount=cents, - description=participant.username) - - log(msg + "succeeded.") - error = "" - except balanced.exc.HTTPError as err: - error = err.message.message - except: - error = repr(sys.exc_info()[1]) - - if error: - log(msg + "failed: %s" % error) - - self.record_exchange('ach', -credit_amount, fee, error, participant) - return error - - - def charge_on_balanced(self, username, balanced_customer_href, amount): - """We have a purported balanced_customer_href. Try to use it. - """ - typecheck( username, unicode - , balanced_customer_href, unicode - , amount, Decimal - ) - - cents, msg, charge_amount, fee = self._prep_hit(amount) - msg = msg % (username, "Balanced") - - try: - customer = balanced.Customer.fetch(balanced_customer_href) - customer.cards.one().debit(amount=cents, description=username) - log(msg + "succeeded.") - error = "" - except balanced.exc.HTTPError as err: - error = err.message.message - except: - error = repr(sys.exc_info()[1]) - - if error: - log(msg + "failed: %s" % error) - - return charge_amount, fee, error - - - def _prep_hit(self, unrounded): - """Takes an amount in dollars. Returns cents, etc. - - cents This is passed to the payment processor charge API. This is - the value that is actually charged to the participant. It's - an int. - msg A log message with a couple %s to be filled in by the - caller. - upcharged Decimal dollar equivalent to `cents'. - fee Decimal dollar amount of the fee portion of `upcharged'. - - The latter two end up in the db in a couple places via record_exchange. - - """ - also_log = '' - rounded = unrounded - if unrounded < MINIMUM_CHARGE: - rounded = MINIMUM_CHARGE # per github/#167 - also_log = ' [rounded up from $%s]' % unrounded - - upcharged, fee = upcharge(rounded) - cents = int(upcharged * 100) - - msg = "Charging %%s %d cents ($%s%s + $%s fee = $%s) on %%s ... " - msg %= cents, rounded, also_log, fee, upcharged - - return cents, msg, upcharged, fee - - # Record-keeping. # =============== - def record_exchange(self, kind, amount, fee, error, participant): - """Given a Bunch of Stuff, return None. - - Records in the exchanges table have these characteristics: - - amount It's negative for credits (representing an outflow from - Gittip to you) and positive for charges. - The sign is how we differentiate the two in, e.g., the - history page. - - fee The payment processor's fee. It's always positive. - - This function takes the result of an API call to a payment processor - and records the result in our db. If the power goes out at this point - then Postgres will be out of sync with the payment processor. We'll - have to resolve that manually be reviewing the transaction log at the - processor and modifying Postgres accordingly. - - For Balanced, this could be automated by generating an ID locally and - commiting that to the db and then passing that through in the meta - field.* Then syncing would be a case of simply:: - - for payment in unresolved_payments: - payment_in_balanced = balanced.Transaction.query.filter( - **{'meta.unique_id': 'value'}).one() - payment.transaction_uri = payment_in_balanced.uri - - * https://www.balancedpayments.com/docs/meta - - """ - - username = participant.username - with self.db.get_cursor() as cursor: - - if error: - amount = fee = Decimal('0.00') - else: - EXCHANGE = """\ - - INSERT INTO exchanges - (amount, fee, participant) - VALUES (%s, %s, %s) - - """ - cursor.execute(EXCHANGE, (amount, fee, username)) - - # Update the participant's balance. - # ================================= - - RESULT = """\ - - UPDATE participants - SET last_{0}_result=%s - , balance=(balance + %s) - WHERE username=%s - RETURNING balance - - """.format(kind) - if kind == 'ach': - amount -= fee - balance = cursor.one(RESULT, (error or '', amount, username)) - if balance < 0: - raise NegativeBalance - - if hasattr(participant, 'set_attributes'): - participant.set_attributes(balance=balance) - - def record_transfer(self, cursor, tipper, tippee, amount, context): cursor.run("""\ diff --git a/gittip/models/participant.py b/gittip/models/participant.py index 0d371b22c3..6d3bcc1878 100644 --- a/gittip/models/participant.py +++ b/gittip/models/participant.py @@ -276,14 +276,12 @@ def close(self, disbursement_strategy): def withdraw_balance_to_bank_account(self, cursor): - from gittip.billing.payday import Payday - hack = Payday(self.db) # Our payout code is on the Payday object. Rather than - # refactor right now, let's just use it from there. - - hack.ach_credit( self - , Decimal('0.00') # don't withhold anything - , Decimal('0.00') # send it all - ) # XXX Records the exchange using a different cursor. :-/ + from gittip.billing.exchanges import ach_credit + ach_credit( self.db + , self + , Decimal('0.00') # don't withhold anything + , Decimal('0.00') # send it all + ) class NoOneToGiveFinalGiftTo(Exception): pass From d2345c32b26351014ddd84568e5539334d3e0ea5 Mon Sep 17 00:00:00 2001 From: Changaco Date: Sun, 22 Jun 2014 12:54:37 +0200 Subject: [PATCH 10/97] update and move tests --- tests/py/test_billing_exchanges.py | 230 +++++++++++++++++++++++++++ tests/py/test_billing_payday.py | 247 +---------------------------- 2 files changed, 238 insertions(+), 239 deletions(-) create mode 100644 tests/py/test_billing_exchanges.py diff --git a/tests/py/test_billing_exchanges.py b/tests/py/test_billing_exchanges.py new file mode 100644 index 0000000000..91364cecf9 --- /dev/null +++ b/tests/py/test_billing_exchanges.py @@ -0,0 +1,230 @@ +from __future__ import absolute_import, division, print_function, unicode_literals + +from decimal import Decimal as D + +import balanced +import mock +import pytest + +from aspen.utils import typecheck +from gittip.billing.exchanges import ( + _prep_hit, + charge, + charge_on_balanced, + record_exchange, + skim_credit, +) +from gittip.exceptions import NegativeBalance, NoBalancedCustomerHref +from gittip.models.participant import Participant +from gittip.testing import Harness +from gittip.testing.balanced import BalancedHarness + + +class TestCharge(BalancedHarness): + + def test_charge_without_cc_details_raises_NoBalancedCustomerHref(self): + alice = self.make_participant('alice') + with self.assertRaises(NoBalancedCustomerHref): + charge(self.db, alice, D('1.00')) + + @mock.patch('gittip.billing.exchanges.charge_on_balanced') + def test_charge_failure_returns_error(self, cob): + cob.return_value = (D('10.00'), D('0.68'), 'FAILED') + actual = charge(self.db, self.janet, D('1.00')) + assert actual == 'FAILED' + + @mock.patch('gittip.billing.exchanges.charge_on_balanced') + def test_charge_success_returns_empty_string(self, charge_on_balanced): + charge_on_balanced.return_value = (D('10.00'), D('0.68'), "") + actual = charge(self.db, self.janet, D('1.00')) + assert actual == '' + + @mock.patch('gittip.billing.exchanges.charge_on_balanced') + def test_charge_success_updates_participant(self, cob): + cob.return_value = (D('10.00'), D('0.68'), "") + charge(self.db, self.janet, D('1.00')) + + janet = Participant.from_username('janet') + expected = {'balance': D('9.32'), 'last_bill_result': ''} + actual = {'balance': janet.balance, + 'last_bill_result': janet.last_bill_result} + assert actual == expected + + +class TestChargeOnBalanced(BalancedHarness): + + def test_charge_on_balanced(self): + actual = charge_on_balanced( 'janet' + , self.janet_href + , D('10.00') # $10.00 USD + ) + assert actual == (D('10.61'), D('0.61'), '') + + def test_charge_on_balanced_small_amount(self): + actual = charge_on_balanced( 'janet' + , self.janet_href + , D('0.06') # $0.06 USD + ) + assert actual == (D('10.00'), D('0.59'), '') + + def test_charge_on_balanced_failure(self): + customer_with_bad_card = self.make_balanced_customer() + card = balanced.Card( + number='4444444444444448', + expiration_year=2020, + expiration_month=12 + ).save() + card.associate_to_customer(customer_with_bad_card) + + actual = charge_on_balanced( 'whatever username' + , customer_with_bad_card + , D('10.00') + ) + assert actual == (D('10.61'), D('0.61'), '402 Client Error: PAYMENT REQUIRED') + + def test_charge_on_balanced_handles_MultipleFoundError(self): + customer_href = self.make_balanced_customer() + card = balanced.Card( + number='4242424242424242', + expiration_year=2020, + expiration_month=12 + ).save() + card.associate_to_customer(customer_href) + + card = balanced.Card( + number='4242424242424242', + expiration_year=2030, + expiration_month=12 + ).save() + card.associate_to_customer(customer_href) + + actual = charge_on_balanced( 'whatever username' + , customer_href + , D('10.00') + ) + assert actual == (D('10.61'), D('0.61'), 'MultipleResultsFound()') + + def test_charge_on_balanced_handles_NotFoundError(self): + customer_with_no_card = self.make_balanced_customer() + actual = charge_on_balanced( 'whatever username' + , customer_with_no_card + , D('10.00') + ) + assert actual == (D('10.61'), D('0.61'), 'NoResultFound()') + + +class TestFees(Harness): + + def prep(self, amount): + """Given a dollar amount as a string, return a 3-tuple. + + The return tuple is like the one returned from _prep_hit, but with the + second value, a log message, removed. + + """ + typecheck(amount, unicode) + out = list(_prep_hit(D(amount))) + out = [out[0]] + out[2:] + return tuple(out) + + def test_prep_hit_basically_works(self): + actual = _prep_hit(D('20.00')) + expected = (2091, + u'Charging %s 2091 cents ($20.00 + $0.91 fee = $20.91) on %s ' u'... ', + D('20.91'), D('0.91')) + assert actual == expected + + def test_prep_hit_full_in_rounded_case(self): + actual = _prep_hit(D('5.00')) + expected = (1000, + u'Charging %s 1000 cents ($9.41 [rounded up from $5.00] + ' u'$0.59 fee = $10.00) on %s ... ', + D('10.00'), D('0.59')) + assert actual == expected + + def test_prep_hit_at_ten_dollars(self): + actual = self.prep(u'10.00') + expected = (1061, D('10.61'), D('0.61')) + assert actual == expected + + def test_prep_hit_at_forty_cents(self): + actual = self.prep(u'0.40') + expected = (1000, D('10.00'), D('0.59')) + assert actual == expected + + def test_prep_hit_at_fifty_cents(self): + actual = self.prep(u'0.50') + expected = (1000, D('10.00'), D('0.59')) + assert actual == expected + + def test_prep_hit_at_sixty_cents(self): + actual = self.prep(u'0.60') + expected = (1000, D('10.00'), D('0.59')) + assert actual == expected + + def test_prep_hit_at_eighty_cents(self): + actual = self.prep(u'0.80') + expected = (1000, D('10.00'), D('0.59')) + assert actual == expected + + def test_prep_hit_at_nine_fifteen(self): + actual = self.prep(u'9.15') + expected = (1000, D('10.00'), D('0.59')) + assert actual == expected + + def test_prep_hit_at_nine_forty(self): + actual = self.prep(u'9.40') + expected = (1000, D('10.00'), D('0.59')) + assert actual == expected + + def test_prep_hit_at_nine_forty_one(self): + actual = self.prep(u'9.41') + expected = (1000, D('10.00'), D('0.59')) + assert actual == expected + + def test_prep_hit_at_nine_forty_two(self): + actual = self.prep(u'9.42') + expected = (1002, D('10.02'), D('0.60')) + assert actual == expected + + def test_skim_credit(self): + actual = skim_credit(D('10.00')) + assert actual == (D('10.00'), D('0.00')) + + +class TestRecordExchange(Harness): + + def test_record_exchange_updates_balance(self): + alice = self.make_participant('alice') + record_exchange( self.db + , 'bill' + , amount=D("0.59") + , fee=D("0.41") + , error="" + , participant=alice + ) + alice = Participant.from_username('alice') + assert alice.balance == D("0.59") + + def test_record_exchange_fails_if_negative_balance(self): + alice = self.make_participant('alice') + pytest.raises( NegativeBalance + , record_exchange + , self.db + , 'ach' + , amount=D("-10.00") + , fee=D("0.41") + , error="" + , participant=alice + ) + + def test_record_exchange_doesnt_update_balance_if_error(self): + alice = self.make_participant('alice') + record_exchange( self.db + , 'bill' + , amount=D("1.00") + , fee=D("0.41") + , error="SOME ERROR" + , participant=alice + ) + alice = Participant.from_username('alice') + assert alice.balance == D("0.00") diff --git a/tests/py/test_billing_payday.py b/tests/py/test_billing_payday.py index 1556864829..054d780cf1 100644 --- a/tests/py/test_billing_payday.py +++ b/tests/py/test_billing_payday.py @@ -4,11 +4,10 @@ import balanced import mock -import pytest from psycopg2 import IntegrityError -from aspen.utils import typecheck, utcnow -from gittip.billing.payday import Payday, skim_credit +from aspen.utils import utcnow +from gittip.billing.payday import Payday from gittip.exceptions import NegativeBalance from gittip.models.participant import Participant from gittip.testing import Harness @@ -26,66 +25,9 @@ def fetch_payday(self): return self.db.one("SELECT * FROM paydays", back_as=dict) -class TestPaydayCharge(PaydayHarness): +class TestPayday(PaydayHarness): - def get_numbers(self): - """Return a list of 11 ints: - - nachs - nach_failing - nactive - ncc_failing - ncc_missing - ncharges - npachinko - nparticipants - ntippers - ntips - ntransfers - - """ - payday = self.fetch_payday() - keys = [key for key in sorted(payday) if key.startswith('n')] - return [payday[key] for key in keys] - - def test_charge_without_cc_details_returns_None(self): - self.payday.start() - actual = self.payday.charge(self.alice, D('1.00')) - assert actual is None - - def test_charge_without_cc_marked_as_failure(self): - self.payday.start() - self.payday.charge(self.alice, D('1.00')) - actual = self.get_numbers() - assert actual == [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0] - - @mock.patch('gittip.billing.payday.Payday.charge_on_balanced') - def test_charge_failure_returns_None(self, cob): - cob.return_value = (D('10.00'), D('0.68'), 'FAILED') - self.payday.start() - actual = self.payday.charge(self.janet, D('1.00')) - assert actual is None - - @mock.patch('gittip.billing.payday.Payday.charge_on_balanced') - def test_charge_success_returns_None(self, charge_on_balanced): - charge_on_balanced.return_value = (D('10.00'), D('0.68'), "") - self.payday.start() - actual = self.payday.charge(self.janet, D('1.00')) - assert actual is None - - @mock.patch('gittip.billing.payday.Payday.charge_on_balanced') - def test_charge_success_updates_participant(self, cob): - cob.return_value = (D('10.00'), D('0.68'), "") - self.payday.start() - self.payday.charge(self.janet, D('1.00')) - - janet = Participant.from_username('janet') - expected = {'balance': D('9.32'), 'last_bill_result': ''} - actual = {'balance': janet.balance, - 'last_bill_result': janet.last_bill_result} - assert actual == expected - - @mock.patch('gittip.billing.payday.Payday.charge_on_balanced') + @mock.patch('gittip.billing.exchanges.charge_on_balanced') def test_payday_moves_money(self, charge_on_balanced): charge_on_balanced.return_value = (D('10.00'), D('0.68'), "") self.janet.set_tip_to(self.homer, '6.00') # under $10! @@ -97,7 +39,7 @@ def test_payday_moves_money(self, charge_on_balanced): assert homer.balance == D('6.00') assert janet.balance == D('3.32') - @mock.patch('gittip.billing.payday.Payday.charge_on_balanced') + @mock.patch('gittip.billing.exchanges.charge_on_balanced') def test_payday_doesnt_move_money_from_a_suspicious_account(self, charge_on_balanced): charge_on_balanced.return_value = (D('10.00'), D('0.68'), "") self.db.run(""" @@ -114,7 +56,7 @@ def test_payday_doesnt_move_money_from_a_suspicious_account(self, charge_on_bala assert janet.balance == D('0.00') assert homer.balance == D('0.00') - @mock.patch('gittip.billing.payday.Payday.charge_on_balanced') + @mock.patch('gittip.billing.exchanges.charge_on_balanced') def test_payday_doesnt_move_money_to_a_suspicious_account(self, charge_on_balanced): charge_on_balanced.return_value = (D('10.00'), D('0.68'), "") self.db.run(""" @@ -145,78 +87,16 @@ def test_payday_moves_money_with_balanced(self): homer_customer = balanced.Customer.fetch(homer.balanced_customer_href) homer_credits = homer_customer.credits.all() - assert len(homer_credits) == 1 + assert len(homer_credits) >= 1 assert homer_credits[0].amount == 1500 assert homer_credits[0].description == 'homer' janet_debits = janet_customer.debits.all() - assert len(janet_debits) == 1 + assert len(janet_debits) >= 1 assert janet_debits[0].amount == 1576 # base amount + fee assert janet_debits[0].description == 'janet' -class TestPaydayChargeOnBalanced(PaydayHarness): - - def test_charge_on_balanced(self): - actual = self.payday.charge_on_balanced( 'janet' - , self.janet_href - , D('10.00') # $10.00 USD - ) - assert actual == (D('10.61'), D('0.61'), '') - - def test_charge_on_balanced_small_amount(self): - actual = self.payday.charge_on_balanced( 'janet' - , self.janet_href - , D('0.06') # $0.06 USD - ) - assert actual == (D('10.00'), D('0.59'), '') - - def test_charge_on_balanced_failure(self): - customer_with_bad_card = self.make_balanced_customer() - card = balanced.Card( - number='4444444444444448', - expiration_year=2020, - expiration_month=12 - ).save() - card.associate_to_customer(customer_with_bad_card) - - actual = self.payday.charge_on_balanced( 'whatever username' - , customer_with_bad_card - , D('10.00') - ) - assert actual == (D('10.61'), D('0.61'), '402 Client Error: PAYMENT REQUIRED') - - def test_charge_on_balanced_handles_MultipleFoundError(self): - customer_href = self.make_balanced_customer() - card = balanced.Card( - number='4242424242424242', - expiration_year=2020, - expiration_month=12 - ).save() - card.associate_to_customer(customer_href) - - card = balanced.Card( - number='4242424242424242', - expiration_year=2030, - expiration_month=12 - ).save() - card.associate_to_customer(customer_href) - - actual = self.payday.charge_on_balanced( 'whatever username' - , customer_href - , D('10.00') - ) - assert actual == (D('10.61'), D('0.61'), 'MultipleResultsFound()') - - def test_charge_on_balanced_handles_NotFoundError(self): - customer_with_no_card = self.make_balanced_customer() - actual = self.payday.charge_on_balanced( 'whatever username' - , customer_with_no_card - , D('10.00') - ) - assert actual == (D('10.61'), D('0.61'), 'NoResultFound()') - - class TestBillingCharges(PaydayHarness): def test_mark_missing_funding(self): @@ -240,80 +120,6 @@ def test_mark_charge_failed(self): assert after['ncc_failing'] == fail_count + 1 -class TestPrepHit(PaydayHarness): - - def prep(self, amount): - """Given a dollar amount as a string, return a 3-tuple. - - The return tuple is like the one returned from _prep_hit, but with the - second value, a log message, removed. - - """ - typecheck(amount, unicode) - out = list(self.payday._prep_hit(D(amount))) - out = [out[0]] + out[2:] - return tuple(out) - - def test_prep_hit_basically_works(self): - actual = self.payday._prep_hit(D('20.00')) - expected = (2091, - u'Charging %s 2091 cents ($20.00 + $0.91 fee = $20.91) on %s ' u'... ', - D('20.91'), D('0.91')) - assert actual == expected - - def test_prep_hit_full_in_rounded_case(self): - actual = self.payday._prep_hit(D('5.00')) - expected = (1000, - u'Charging %s 1000 cents ($9.41 [rounded up from $5.00] + ' u'$0.59 fee = $10.00) on %s ... ', - D('10.00'), D('0.59')) - assert actual == expected - - def test_prep_hit_at_ten_dollars(self): - actual = self.prep(u'10.00') - expected = (1061, D('10.61'), D('0.61')) - assert actual == expected - - def test_prep_hit_at_forty_cents(self): - actual = self.prep(u'0.40') - expected = (1000, D('10.00'), D('0.59')) - assert actual == expected - - def test_prep_hit_at_fifty_cents(self): - actual = self.prep(u'0.50') - expected = (1000, D('10.00'), D('0.59')) - assert actual == expected - - def test_prep_hit_at_sixty_cents(self): - actual = self.prep(u'0.60') - expected = (1000, D('10.00'), D('0.59')) - assert actual == expected - - def test_prep_hit_at_eighty_cents(self): - actual = self.prep(u'0.80') - expected = (1000, D('10.00'), D('0.59')) - assert actual == expected - - def test_prep_hit_at_nine_fifteen(self): - actual = self.prep(u'9.15') - expected = (1000, D('10.00'), D('0.59')) - assert actual == expected - - def test_prep_hit_at_nine_forty(self): - actual = self.prep(u'9.40') - expected = (1000, D('10.00'), D('0.59')) - assert actual == expected - - def test_prep_hit_at_nine_forty_one(self): - actual = self.prep(u'9.41') - expected = (1000, D('10.00'), D('0.59')) - assert actual == expected - - def test_prep_hit_at_nine_forty_two(self): - actual = self.prep(u'9.42') - expected = (1002, D('10.02'), D('0.60')) - assert actual == expected - - class TestBillingPayday(PaydayHarness): def test_move_pending_to_balance_for_teams_does_so(self): @@ -515,10 +321,6 @@ def test_debit_participant(self): with self.assertRaises(NegativeBalance): self.payday.debit_participant(cursor, subject.username, amount) - def test_skim_credit(self): - actual = skim_credit(D('10.00')) - assert actual == (D('10.00'), D('0.00')) - def test_credit_participant(self): amount = D('1.00') subject = self.make_participant('test_credit_participant', pending=0, @@ -573,39 +375,6 @@ def test_record_transfer_invalid_participant(self): , 'tip' ) - def test_record_exchange_updates_balance(self): - alice = Participant.from_username('alice') - self.payday.record_exchange( 'bill' - , amount=D("0.59") - , fee=D("0.41") - , error="" - , participant=alice - ) - alice = Participant.from_username('alice') - assert alice.balance == D("0.59") - - def test_record_exchange_fails_if_negative_balance(self): - alice = Participant.from_username('alice') - pytest.raises( NegativeBalance - , self.payday.record_exchange - , 'ach' - , amount=D("-10.00") - , fee=D("0.41") - , error="" - , participant=alice - ) - - def test_record_exchange_doesnt_update_balance_if_error(self): - alice = Participant.from_username('alice') - self.payday.record_exchange( 'bill' - , amount=D("1.00") - , fee=D("0.41") - , error="SOME ERROR" - , participant=alice - ) - alice = Participant.from_username('alice') - assert alice.balance == D("0.00") - class TestPachinko(Harness): From 19e8a54089cfbea421ac8a4601df4d71cc58c9ed Mon Sep 17 00:00:00 2001 From: Changaco Date: Sun, 22 Jun 2014 19:27:21 +0200 Subject: [PATCH 11/97] update test fixtures --- tests/py/fixtures/BalancedHarness.yml | 128 ++-- tests/py/fixtures/TestBalancedBankAccount.yml | 170 ++--- tests/py/fixtures/TestBalancedCard.yml | 674 +++++++++--------- tests/py/fixtures/TestBillingAssociate.yml | 462 ++++++------ tests/py/fixtures/TestBillingClear.yml | 166 ++--- tests/py/fixtures/TestBillingPayday.yml | 148 ---- ...nBalanced.yml => TestChargeOnBalanced.yml} | 420 +++++------ tests/py/fixtures/TestClosing.yml | 104 +-- tests/py/fixtures/TestPayday.yml | 286 ++++++++ tests/py/fixtures/TestPaydayCharge.yml | 280 -------- 10 files changed, 1348 insertions(+), 1490 deletions(-) delete mode 100644 tests/py/fixtures/TestBillingPayday.yml rename tests/py/fixtures/{TestPaydayChargeOnBalanced.yml => TestChargeOnBalanced.yml} (55%) create mode 100644 tests/py/fixtures/TestPayday.yml delete mode 100644 tests/py/fixtures/TestPaydayCharge.yml diff --git a/tests/py/fixtures/BalancedHarness.yml b/tests/py/fixtures/BalancedHarness.yml index a8cbaeb1b3..496ee65a20 100644 --- a/tests/py/fixtures/BalancedHarness.yml +++ b/tests/py/fixtures/BalancedHarness.yml @@ -6,10 +6,10 @@ interactions: uri: https://api.balancedpayments.com:443/api_keys response: body: {string: !!python/unicode "{\n \"links\": {},\n \"api_keys\": [\n {\n - \ \"links\": {},\n \"created_at\": \"2014-07-01T11:32:21.176697Z\",\n - \ \"secret\": \"ak-test-i2BYNzhi88y5uxqw4Rcw738sk2rjLWiY\",\n \"href\": - \"/api_keys/AK2QkRBCbioFJrtCGdS61egF\",\n \"meta\": {},\n \"id\": - \"AK2QkRBCbioFJrtCGdS61egF\"\n }\n ]\n}"} + \ \"links\": {},\n \"created_at\": \"2014-07-01T15:43:42.734567Z\",\n + \ \"secret\": \"ak-test-W2FghBPvv0YNgaln7waeGwjVoH7oq1A6\",\n \"href\": + \"/api_keys/AK3JDxCRGTrpx6szS4xRqFxn\",\n \"meta\": {},\n \"id\": + \"AK3JDxCRGTrpx6szS4xRqFxn\"\n }\n ]\n}"} headers: content-length: ['289'] content-type: [application/json] @@ -22,20 +22,20 @@ interactions: response: body: string: !!binary | - H4sIAAAAAAAAA5VUTW/bMAy991cEPuyyJfFngQQotrXZZe3QYE0xbMNgyBaNeJElQ5aabEH++2hZ - rmNMKLZLQFKP5CP54uPFZOJVoIi3nBzRRo+RRqHnzSsid6BqRnJo3rKyKtVVmLwSRdGAuvK9Nx2c - w6GFc82YjWwlFP9RwFRGfJjY/K4DRnwbqCU8lUI34zZKKMIwFFhUUcp/I468T22Odz4gFvpuBuq2 - gK8lT6HJpdi3PXw/DhZ+2LXCRyoqggAtWwYeHEhVM5jlorJrQQgnFbSPa52xMp+s4Gl4YyXfteP0 - zRAu9hxkmutGiQpkm3jzGOwfi693q+xud7++pcFn+L29PmSe4WmHMCdzbny++fCwmX5aB/vNr8Pq - /cfbG/Jl/fP6Ib5nZUwHLrkEooCmxFw99IN46ifTMN740TK8XIaL2WIRxVH8bUhpdF0LqVLAJbCU - UCqhacfx7MM750J0TV9qFPmzKIj8JHA0qreCQ8p1lXWreR1cJn6SJEEYxQOtWgqqc1UKjlwKwhp4 - vlcvcXN4szPNUcaK4eAFQEveqq29PG1neXl9pswJf38YKY0POlLWjEJWKrOeuTWtYs/1N0ONg2yQ - tAEOngtLy6bWyrDGmr3jQvZ66qCD58RKoD1RFIWxnTgiqa1nLBdGSIrTGH7WdKFyzE+3gp2Vs64L - jZ8VzS20t124v/9J8+fB58fR0sfQk3MUJQlviJFVN9Ao4CKQEb5LSZ4Lze3dxxFXTk4Yy0huvgre - fPBcWFRKX9ia7Sft4vQHQlHAFs0FAAA= + H4sIAAAAAAAAA5VUTW/bMAy991cEPuyyNfFnhwQotrXZZe3QYE0xbMNgyBaNeJElQ5aadEH++2hZ + rmNMKLZLQFKP5CP54sPZZOJVoIi3mBzQRo+RRqHnzSoit6BqRnJo3rGyKtVlmLwSRdGAuvS9Nx2c + w76Fc82YjWwkFP9RwFRGfJjY/K4DRnwbqCU8lkI34zZKKMIwFFhUUcp/I468j22OdzogFvphBuq2 + gK8lT6HJpdi1PXw/Cd+GcdcKH6moCAK0bBl4sCdVzWCai8quBSGcVNA+rnTGynyyhMfhjZV8247T + N0O42HGQaa4bJSqQbeL1Q7B7KL7dLrPb7d3qhgZf4Pfmap95hqcdwpzMufHZ+uP9+vzzKtitn/bL + D59ursnX1a+r+/iOlTEduOQSiAKaEnP10A/icz85D+O1Hy3Ci0U4n87nURzF34eURte1kCoFXAJL + CaUSmnYczz68dy5E1/SlRpE/jYLITwJHo3ojOKRcV1m3mtfBReInSRKEUTzQqqWgOlel4MilIKyB + 53v1EjeHNzvTHGWsGA5eALTkrdray9N2lpfXZ8oc8fenkdL4oCNlTSlkpTLrmVnTKvZUf1PUOMgG + SRvg4LmwtGxqrQxrrNk7LmSvpw46eE6sBNoTRVEY24kjktp6xnJhhKQ4jeFnTRcqx/x0I9hJOeu6 + 0PhZ0dxCe9uF+/ufNHsefHYYLX0MPTpHUZLwhhhZdQONAi4CGeHblOS50NzefRxx5eSEsYzk5qvg + zQbPhUWl9IWt2X7Szo5/AKMg5n7NBQAA headers: content-encoding: [gzip] - content-length: ['593'] + content-length: ['594'] content-type: [application/json] status: {code: 200, message: OK} - request: @@ -46,9 +46,9 @@ interactions: response: body: {string: !!python/unicode "{\n \"customers\": [\n {\n \"name\": null,\n \"links\": {\n \"source\": null,\n \"destination\": - null\n },\n \"updated_at\": \"2014-07-01T11:32:21.952391Z\",\n \"created_at\": - \"2014-07-01T11:32:21.838223Z\",\n \"dob_month\": null,\n \"id\": - \"CU2R4ZqaRKDZoW1GpfgeKFbr\",\n \"phone\": null,\n \"href\": \"/customers/CU2R4ZqaRKDZoW1GpfgeKFbr\",\n + null\n },\n \"updated_at\": \"2014-07-01T15:43:43.537316Z\",\n \"created_at\": + \"2014-07-01T15:43:43.416311Z\",\n \"dob_month\": null,\n \"id\": + \"CU3KoKRb123DPYH3y6rw4OmF\",\n \"phone\": null,\n \"href\": \"/customers/CU3KoKRb123DPYH3y6rw4OmF\",\n \ \"merchant_status\": \"no-match\",\n \"meta\": {\n \"seq\": \"0\"\n },\n \"dob_year\": null,\n \"address\": {\n \"city\": null,\n \"line2\": null,\n \"line1\": null,\n \"state\": @@ -77,9 +77,9 @@ interactions: response: body: {string: !!python/unicode "{\n \"customers\": [\n {\n \"name\": null,\n \"links\": {\n \"source\": null,\n \"destination\": - null\n },\n \"updated_at\": \"2014-07-01T11:32:22.451880Z\",\n \"created_at\": - \"2014-07-01T11:32:22.324563Z\",\n \"dob_month\": null,\n \"id\": - \"CU2RCigJCqQDdYQEcThv3Jxr\",\n \"phone\": null,\n \"href\": \"/customers/CU2RCigJCqQDdYQEcThv3Jxr\",\n + null\n },\n \"updated_at\": \"2014-07-01T15:43:44.133689Z\",\n \"created_at\": + \"2014-07-01T15:43:43.910534Z\",\n \"dob_month\": null,\n \"id\": + \"CU3KXdyvNSDeyLP3pAWr9hqd\",\n \"phone\": null,\n \"href\": \"/customers/CU3KXdyvNSDeyLP3pAWr9hqd\",\n \ \"merchant_status\": \"no-match\",\n \"meta\": {\n \"seq\": \"1\"\n },\n \"dob_year\": null,\n \"address\": {\n \"city\": null,\n \"line2\": null,\n \"line1\": null,\n \"state\": @@ -114,17 +114,17 @@ interactions: \ \"cvv_result\": null,\n \"number\": \"xxxxxxxxxxxx1111\",\n \"avs_postal_match\": \"no\",\n \"expiration_month\": 10,\n \"meta\": {\n \"region\": \"Confusion\",\n \"city_town\": \"\",\n \"address_2\": \"Box - 2\"\n },\n \"id\": \"CC2SGPDNPBU0oOKbaes9tqC1\",\n \"category\": + 2\"\n },\n \"id\": \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \"category\": \"other\",\n \"type\": \"credit\",\n \"cvv_match\": null,\n \"bank_name\": \"JPMORGAN CHASE BANK, N.A.\",\n \"avs_street_match\": \"yes\",\n \"brand\": - \"Visa\",\n \"updated_at\": \"2014-07-01T11:32:23.284439Z\",\n \"address\": + \"Visa\",\n \"updated_at\": \"2014-07-01T15:43:45.463915Z\",\n \"address\": {\n \"city\": null,\n \"line2\": null,\n \"line1\": \"123 Main Street\",\n \"state\": \"Confusion\",\n \"postal_code\": \"90210\",\n \"country_code\": null\n },\n \"can_debit\": true,\n \"name\": null,\n \"expiration_year\": 2020,\n \"cvv\": null,\n \"is_verified\": true,\n \"avs_result\": \"Street address and postal code do not match.\",\n \"can_credit\": false,\n \"href\": - \"/cards/CC2SGPDNPBU0oOKbaes9tqC1\",\n \"created_at\": \"2014-07-01T11:32:23.284437Z\"\n + \"/cards/CC3MHBsW6vHaL3suLVdTVbO9\",\n \"created_at\": \"2014-07-01T15:43:45.463913Z\"\n \ }\n ],\n \"links\": {\n \"cards.card_holds\": \"/cards/{cards.id}/card_holds\",\n \ \"cards.customer\": \"/customers/{cards.customer}\",\n \"cards.disputes\": \"/cards/{cards.id}/disputes\",\n \"cards.debits\": \"/cards/{cards.id}/debits\"\n @@ -137,38 +137,38 @@ interactions: content-type: [application/json] status: {code: 201, message: CREATED} - request: - body: '{"links": {"customer": "/customers/CU2RCigJCqQDdYQEcThv3Jxr"}, "cvv_result": + body: '{"links": {"customer": "/customers/CU3KXdyvNSDeyLP3pAWr9hqd"}, "cvv_result": null, "number": "xxxxxxxxxxxx1111", "expiration_month": 10, "meta": {"region": - "Confusion", "city_town": "", "address_2": "Box 2"}, "id": "CC2SGPDNPBU0oOKbaes9tqC1", + "Confusion", "city_town": "", "address_2": "Box 2"}, "id": "CC3MHBsW6vHaL3suLVdTVbO9", "category": "other", "is_verified": true, "type": "credit", "cvv_match": null, "bank_name": "JPMORGAN CHASE BANK, N.A.", "avs_street_match": "yes", "brand": - "Visa", "updated_at": "2014-07-01T11:32:23.284439Z", "fingerprint": "8c7f0423365af88c3e36cf02746eca64fdcf36ddc2e1f398c7ec9f3ffd05a267", + "Visa", "updated_at": "2014-07-01T15:43:45.463915Z", "fingerprint": "8c7f0423365af88c3e36cf02746eca64fdcf36ddc2e1f398c7ec9f3ffd05a267", "can_debit": true, "name": null, "expiration_year": 2020, "can_credit": false, "avs_postal_match": "no", "avs_result": "Street address and postal code do not - match.", "cvv": null, "created_at": "2014-07-01T11:32:23.284437Z", "address": + match.", "cvv": null, "created_at": "2014-07-01T15:43:45.463913Z", "address": {"city": null, "line2": null, "line1": "123 Main Street", "state": "Confusion", "postal_code": "90210", "country_code": null}}' headers: {} method: PUT - uri: https://api.balancedpayments.com:443/cards/CC2SGPDNPBU0oOKbaes9tqC1 + uri: https://api.balancedpayments.com:443/cards/CC3MHBsW6vHaL3suLVdTVbO9 response: body: string: !!binary | - H4sIAAAAAAAAA41U207jMBB95yusPEObOKFN+1YKYgWi3FdaVqvI9aW1SO1iO91WqP++tpM0CbDa - 9UOkzMVz5szxvB8BEGCkiA7G4Kf9AeDdf6055+LVmWuDiyy0kSuqrDWYPsOHKV9cTd/uz8mP+wv8 - tNzEV1sVVPn74/oixsWCqrXiwrjEFA9ZmMA4HpwilqY4pvEAsxAOkwHFaJAwglk8IARDGrF4ZMMp - HrGYMRKeIjgYBoeL8WaTKaqL3N0rijw/eESxmpcwt60T2dNko43O1lIblGcrZPDSYROy8dPtmitk - uBTZSgrj/FF4qLCiBnXJUXRhYz01UrBCu5863HHHzS4z8rePaDsQIbYJnUHnOJNbAD9TyIm/dwof - L+/OZ3dnz6G8vZ4jqkfmbdrqCSNDF1LtXLQ0S0vBAbDZrakzY0UJN10S6/47HM6ReM0EWvmsq7ub - 24fLyQxMv00eL8DZZHZ9DGa9Sa/LpzaKUtPwuaO6CZgrJHwf37lGjblYE4uaZMirA4ZRchIOT8Lo - KYrGMRzDuJemKUxGL61SJWU2vi1OS7C1tHsoVUwds1+YI1cvgjG4QVyARw+9PRgrDeOb/3KelXSw - JD5mFMIobGdjWQijdlkV4Op/ehkYiYzQuR3HGBhV0Ea/Je2debT0uKPIvUEYwkaQ9jF8aDPgOttQ - xRmnjvZOASf+w9MJyt5BpURgxwTK9oBDD4gEQhrgVdIauENfqWkMGMp1g3+pKHOs9P1u6f+HcBX9 - hwhgmiTx8KV8G3tL5S/H1octVe6ynquaLWXu11oN4t1j6XGy96gqf0l5ndfab/162el+lVkb9tWY - qyTC9bowVuhfljp4O4X80P+WUfpsc/uj/R/ViBIhoQUAAA== + H4sIAAAAAAAAA41UW0/bMBR+51dYeYY2idP08lbYNDSgQ4PBxDRFri/UIrUz2+mIUP/7bCdpEmDa + /BAp5+Lzne98Pi9HAAQYKaKDBfhhfwB48V9rzrl4cubW4CJLbeSWKmsNzr7Bi++k2q1uPtDq8hoW + y3s13/wiQZO/P24vYlw8UlUoLoxLnOEpC5MYwnSC2GyGIYUpZmE8TVKKUZowghlMCcExjRic23CK + 5wwyRsIJitNpcLgY73aZorrM3b2izPODR5TbdQ3zuXcie7pstNNZIbVBebZFBm8cNiE7P30uuEKG + S5FtpTDOH4WHCltq0JAcRR9trKdGClZq99OGO+64qTIjf/uIvgMRYpvQWewcp/IZxG8p5MTfewav + zk/1fbo7R5dQl5d35PZu/WXeYwQZ+ihV5aKl2VgKDoBNVVBnxooSboYktv0POFwj8ZQJtPVZn6+v + vnz9tFyBs/PlzUdwulxdHIPVaDka8qmNotR0fFZUdwFrhYTv445r1JnLgljUJENeHXEYJSfh9CSM + bqPJIoGLZDKaTaIYRg+9UjVlNr4vTkuwtfR7qFVMHbPvmCNXz94MrhAX4MZD7w/GSsP45t+dZyMd + LImPmYdxFPazsSyFUVXWBLj6b14GRiIjdG3HsQBGlbTTb037YB49PVYUuTcYh3EnSPsYXrUZcJ3t + qOKMU0f7oIAT/+HpBHXvoFEisGMCdXvAoQdEAiEN8CrpDdyhb9S0AAzlusO/UZQ5VsZ+t4z/Q7iK + /kMESQrnEXyo38beUvnTsfVqS9W7bOSqZhuZ+7XWgnjxWEac7D2qxl9T3ub19tu4XXZ63GS2hn0z + 5iaJcF2Uxgr93VIH76CQH/rfMmqfbW5/tP8Dq5w30qEFAAA= headers: content-encoding: [gzip] - content-length: ['661'] + content-length: ['662'] content-type: [application/json] status: {code: 200, message: OK} - request: @@ -179,9 +179,9 @@ interactions: response: body: {string: !!python/unicode "{\n \"customers\": [\n {\n \"name\": null,\n \"links\": {\n \"source\": null,\n \"destination\": - null\n },\n \"updated_at\": \"2014-07-01T11:32:24.889772Z\",\n \"created_at\": - \"2014-07-01T11:32:24.783468Z\",\n \"dob_month\": null,\n \"id\": - \"CU2Uomy8gvhh6SdpAqQO2wHv\",\n \"phone\": null,\n \"href\": \"/customers/CU2Uomy8gvhh6SdpAqQO2wHv\",\n + null\n },\n \"updated_at\": \"2014-07-01T15:43:46.754060Z\",\n \"created_at\": + \"2014-07-01T15:43:46.652179Z\",\n \"dob_month\": null,\n \"id\": + \"CU3O2IKYocPZ5LA7GkZWpwKZ\",\n \"phone\": null,\n \"href\": \"/customers/CU3O2IKYocPZ5LA7GkZWpwKZ\",\n \ \"merchant_status\": \"no-match\",\n \"meta\": {\n \"seq\": \"2\"\n },\n \"dob_year\": null,\n \"address\": {\n \"city\": null,\n \"line2\": null,\n \"line1\": null,\n \"state\": @@ -213,13 +213,13 @@ interactions: \"121042882\",\n \"bank_name\": \"WELLS FARGO BANK NA\",\n \"account_type\": \"checking\",\n \"name\": \"Homer Jay\",\n \"links\": {\n \"customer\": null,\n \"bank_account_verification\": null\n },\n \"can_credit\": - true,\n \"created_at\": \"2014-07-01T11:32:25.564498Z\",\n \"fingerprint\": + true,\n \"created_at\": \"2014-07-01T15:43:49.076855Z\",\n \"fingerprint\": \"dac6514d9dd3a1cd7ac925d5749e0308c2c65fa27c506aefb0c9b21611ff9969\",\n \"updated_at\": - \"2014-07-01T11:32:25.564501Z\",\n \"href\": \"/bank_accounts/BA2Vg7rBg6MrZmOr6xyqEYcd\",\n + \"2014-07-01T15:43:49.076857Z\",\n \"href\": \"/bank_accounts/BA3QLnsEGgrsjqfhXh43Pq4d\",\n \ \"meta\": {},\n \"account_number\": \"xxx233a\",\n \"address\": {\n \"city\": null,\n \"line2\": null,\n \"line1\": null,\n \ \"state\": null,\n \"postal_code\": null,\n \"country_code\": - null\n },\n \"can_debit\": false,\n \"id\": \"BA2Vg7rBg6MrZmOr6xyqEYcd\"\n + null\n },\n \"can_debit\": false,\n \"id\": \"BA3QLnsEGgrsjqfhXh43Pq4d\"\n \ }\n ],\n \"links\": {\n \"bank_accounts.credits\": \"/bank_accounts/{bank_accounts.id}/credits\",\n \ \"bank_accounts.bank_account_verifications\": \"/bank_accounts/{bank_accounts.id}/verifications\",\n \ \"bank_accounts.customer\": \"/customers/{bank_accounts.customer}\",\n @@ -235,28 +235,28 @@ interactions: status: {code: 201, message: CREATED} - request: body: '{"routing_number": "121042882", "bank_name": "WELLS FARGO BANK NA", "account_type": - "checking", "name": "Homer Jay", "links": {"customer": "/customers/CU2Uomy8gvhh6SdpAqQO2wHv"}, - "can_credit": true, "created_at": "2014-07-01T11:32:25.564498Z", "fingerprint": + "checking", "name": "Homer Jay", "links": {"customer": "/customers/CU3O2IKYocPZ5LA7GkZWpwKZ"}, + "can_credit": true, "created_at": "2014-07-01T15:43:49.076855Z", "fingerprint": "dac6514d9dd3a1cd7ac925d5749e0308c2c65fa27c506aefb0c9b21611ff9969", "updated_at": - "2014-07-01T11:32:25.564501Z", "meta": {}, "account_number": "xxx233a", "address": + "2014-07-01T15:43:49.076857Z", "meta": {}, "account_number": "xxx233a", "address": {"city": null, "line2": null, "line1": null, "state": null, "postal_code": null, - "country_code": null}, "can_debit": false, "id": "BA2Vg7rBg6MrZmOr6xyqEYcd"}' + "country_code": null}, "can_debit": false, "id": "BA3QLnsEGgrsjqfhXh43Pq4d"}' headers: {} method: PUT - uri: https://api.balancedpayments.com:443/bank_accounts/BA2Vg7rBg6MrZmOr6xyqEYcd + uri: https://api.balancedpayments.com:443/bank_accounts/BA3QLnsEGgrsjqfhXh43Pq4d response: body: string: !!binary | - H4sIAAAAAAAAA5VTW2+bMBh9769APK8Bm3veyNSt2rpGW9dW6zQhY5sEhVuNSYMi/vtsCuXSdOpe - /ODzcTiXz8czRVFDlO0ChHFeZbxUl8pvcakox/YUMMsrHmebIKvSkDKBqwAC3YSuC9UP/VDLkaGU - Svz+4urqRvnk//i8Vlb+9Vfl2h8mux8FvC7aYbyleCf4h4me5jJPKVO+oHqAkjjbSYm9OCEPVyWX - g5Lr4y28zdPa3ey3W/uGFP7j9zV8uty/EMzcBnvK4ijGiMd5JgiyKkk6R82LNYyyADNKYi4mOKvo - gDCKOCUBkogKdWCe6865Dn4CsDTgEloLyzZNz30YDETCKWUFi7P2G4KwbQGTeIQYCGDiIOxBi1iO - 6VHd0F0MBR4h6GBLtxGNQh17IQQ2AFHkebY3EFcF+acWewFtz7LMkZYto5EUoU0WQFv58G7jsNXG - /sYe0jWzD/XjxS9Mhn+llCNZwpBRX+qwI4fDARoGGvVOCKPlvLyY113ufaqiItEyhaevwevrkgvj - r6+LXABJgHNyAmx3ndVj9GTvhIZt7RFKyqH3mMjY3sypZWrE+Ud6km5GOzt9bYvnxZKpzGs4TlpZ - xKTR+uHnqGZM4/HJXr+TfPwUyq62udjRU9P6d1dqM6U90JwmaSN9p6Zu9v/8tllO3MwVvhlVo4rC - mrPmL7GaaGsXBQAA + H4sIAAAAAAAAA5VTW2+bMBh9769APK+JbW4hb3Tqsi1R293ULdOEjG0aFmKoMV2iiP8+m0K5NNW6 + Fx58Po7P5fPxzDDMCPNtiAnJSi4Lc278VIeGcay/ChZZKRN+F/JyFzGhcBMiCGw0myHzTTtUc3C8 + Yxq/vVytvhjvgs+La+MiuFoaV0E32VwUykNeD5MNI1vF3020NO+zHRPGR3zooDThWy2xFafkkbKQ + elBzvf1mXaMPyx8ZuVk7q8BbbNe3+Z/l+olg5DZ8YCKJE4JlknFFwMs0bRxVT9YI5iERjCZSTUhR + sg4RDEtGQ6wREwFonwPvHMCv0Jnb1tz2J8BzZ47T3W/GyikTuUh4/Q/FxHWgTX1KLQwJ9TDxkUMd + z/YZsMCMIIXHGHnEAS5mcQSIHyHoQhjHvu/6XTJlTv+hxQGeA/paNoLFWsR0sADTi8D6tOLF5eJO + FL/v4833jW3d3Nu0u2vHJNYldBm1pXY7st/vkWXhXu+UClaMy0vkocm9TVVVpFpm6PQxfH5cSGX8 + +XGeKSANSUZPgPWui0MfPdk7ZVFde4zTous9oTq2F3OqmSr1/aU9aTe9nR2+tsnjYulUxjUcB61M + ElpN2+HHqEZM/fHBXr+SvP8Uiqa2sdjeU5u2766YjpS2QHWapI70lZqa2f/zW2c5cDNW+GJUlakK + q86qvyivOQsXBQAA headers: content-encoding: [gzip] content-length: ['525'] diff --git a/tests/py/fixtures/TestBalancedBankAccount.yml b/tests/py/fixtures/TestBalancedBankAccount.yml index ab996e2f9a..ffe7ccd3ef 100644 --- a/tests/py/fixtures/TestBalancedBankAccount.yml +++ b/tests/py/fixtures/TestBalancedBankAccount.yml @@ -3,44 +3,44 @@ interactions: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2Uomy8gvhh6SdpAqQO2wHv + uri: https://api.balancedpayments.com:443/customers/CU3O2IKYocPZ5LA7GkZWpwKZ response: body: string: !!binary | - H4sIAAAAAAAAA41Uy27bMBC85ysEnevIUoxY9S3opbeiaHJpUQg0yUREJFLhw6lh6N+7lCWZJtWw - Fx12Z4bc2RFPN0mSYqO0aKlU6S75BYUkOQ1faHHUUqhy0zSfplrD+KuFTiCAKWEk9oFQJ1RpxpFm - go8qo0g/q5mOIE1JhTQg0mKdb1br7WqdP+b57q7YFZvbsvy83RY/05mCJY1QtuXd5r50KETsq1Zw - XfvDMGKP/fJUPIn2WL4c6vr+B+ke3r5/K96/Hi5ndrXg/oBpLemzpWezg1lcCYzGNeK6UhppY41M - uVi1SOP6clxLNfIspm+DQWngoJ3tSJH0R0OESKq8TWGmjx4Q9gQrpcVyOQ/L9uK+FyDSCWg0FRZk - oYmF4Voe3W4wyN4ouIdS1VLqlOJVg5Te+GPSFrEmKLKrwPVw1m+bHzupE95L9G/nCKcZuDbkWWWn - ebFjvx9X5BAxkqSqRUOGVTpRcMiM9JmDOyfZ0dgj/lohPJgUkbmGBkqE7llMYsQscN2/9R8+OL/0 - ghn0j6aSQwr+b5gQHlzK2hax5AwJmISpzmgaIc+ogK8l4gph+3hFNK6QgQ48EobHZphAC+wDvMyo - iVxB0gkWKAhJzm/7B+EcMQEXnloSzdMEgv+rv+n/AqhHO/tTBgAA + H4sIAAAAAAAAA41UTY/TMBS876+IcqabpJu2orcVB4QWCQ4gRBGKXNsoVhM7+AOoqvx3ntMk69oB + 7yWH55mJ37zxu9wlSYqN0qKlUqX75BsUkuQyfOGIo5ZClZumeTXVGsZPFjqBAKaEkdgHQp1QpRlH + mgk+qowi/axmOoI0JRXSgEjXeVGu8t0qLz4Vm335sC+397tNmW/zQzpTsKQRynazLnavHQoRx6oV + XNd+M4zY3775/PBh/e7pq8AfD5v3j7u3p8OX7veTI9DVgvsNprWkPyw9mx3M4kpgNK4R15XSSBtr + ZMrFqkUa188ttlQjz2L6czAoDRy0vZ0pkn5riBBJlTcpzPTZA8KcYKR0vVwuwrK9uO8FiHQCDpoK + C7JwiIXhWp7d06CRo1FwD6WqpdQpxasGKV36bdIWsSYospvA9fCv7zY/tlMnvM/Rv58jnGbg2pBn + lV3mwY7n/Tgih4iRJFUtGjKM0omCQ2akzxzcNcmOxhHxU4XwYFJE5hYaKBF6ZDGJEbPAdV/rP3xw + nvSCGfSPppJDCl7WTAgPLmVti1hyhQRMwlRnNI2QZ1TA1xJxhbBdXhGNG2SgA0vC8FgPE2iB/Qs2 + M2oiV5B0ggUKQpLrbv9POEdMwIVVS6J5mkDwvvq7/i9xeE/EUwYAAA== headers: content-encoding: [gzip] - content-length: ['495'] + content-length: ['496'] content-type: [application/json] status: {code: 200, message: OK} - request: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2Uomy8gvhh6SdpAqQO2wHv/bank_accounts?is_valid=True&limit=10&offset=0 + uri: https://api.balancedpayments.com:443/customers/CU3O2IKYocPZ5LA7GkZWpwKZ/bank_accounts?is_valid=True&limit=10&offset=0 response: body: string: !!binary | - H4sIAAAAAAAAA7VUXW/aMBR976+I8tCnFWKTBIKEJpi6VVtXtLXdtE5TZGwHLPJVx2EglP8+2yQk - oWTqS18icc/1veeec7n7C8MwFyhe+wjjJI9FZo6N3zJoGHv9lTBPcsHipR/n0YJyiZsAAsuGoxE0 - 31VJukaMIqrwn9e3t/fGx+n3T3NjNr37YtxN68yykS92qU7GK4rXsn6dUZW5SSLKjc9oV0Mhi9eK - YkVO0sN5JlSiqvXhET4m0W603KxW7j1Jp8/f5vDvzeZY4GRaf0M5CxhGgiWxLBDnYVhOVBxHwyj2 - MaeECZkheE5rhFMkKPGRQkxoAfvKGl5Z4AGA8QCOodNzXNv2Rk/1AIGclPKUs1i/IQi7DrCJR8gA - AUyGCHvQIc7Q9qg1sEYYSjxAcIgdy0U0WFjYW0DgAhAEnud6deE8Jf/l4vag6zmO3eCy4jRQJPqt - BejPpvDHcshnS/crf4rm3N3unq9/YVL3iqhAyoRao8rUeke22y0cDFDDd0I4zU7NY2JX6l6pKi2S - LlN4PgxehjMhB38ZThMJhD5OyBlQ7zrfNdGzvhO60LYHKMxq3xlRsnXqpCsV8vtHzWRWaumwGaJM - O9+v9jbrd21t25f3LPM3KGRk8iB38DJkERMTYF0mQZBRMbFKpc2YblUDtcoHSc2jz2/VUnORPYFV - djxwkpEqkHK6YUmu3G8QE4l0SL0rnwWMv5U4Uny9rWq3Ghekfft62m5F8vQ/sW/9RXqMFP0yt6Te - xpu/WjfmlbWbZymrjG23aJ69xi6dEK2yio4i+qy9ktThBnbQ6ZxYi9map3/CsfNpYSrXLop/NxcR - yKUGAAA= + H4sIAAAAAAAAA7VUXW/aMBR976+I8tCntbHzQQgSmujUsQ3UdlunbkxTZGyneIQkdRwKQvz32SYh + CQWtL32JxD3X9557zuVuzgzDnKJkHiKM0yIRudkzfsugYWz0V8I8LQRLHsOkWEwpl7gJbQhcu9u1 + zXdVkq6RoAVV+MP1ePzd+Dj4Nrw1rgY3I+NmUGeWjUKxznQynlE8l/XrjKrMp3RBufEFrWsoZslc + UazISXq4yIVKVLU+/HBu7c+jXym+m3jjgT+cTx6y59FkX+Bg2nBJOYsYRoKliSyQFHFcTrTdj4ZR + EmJOCRMyQ/CC1ginSFASIoWYNoDuBfAvALyHXs91em5wCfxO1/Pq/mYkJ6U84yzRbwjCHQ+6JCDE + QRATH+HA9ojnuwEFDuhiW+IRsn3sgQ6i0RTgYGrDDoRRFASdoFamyMh/uHjA90CTy4zTSJGwWgtg + XQ2cr+Mkvx4+8vzvUzT7OXOduyeX1L0WVCBlQq1RZWq9I6vVynYc1PCdEE7zQ/OYWJe6V6pKi6TL + 1D4ehi/DuZCDvwxnqQTiEKfkCKh3na+b6FHfCZ1q2yMU57XvjCjZTuqkK23l94+ayazU0mEzRrl2 + 3qr2NrdObW3bl/csD5coZqR/L3fwPGYLJvoQnKdRlFPRB6XSZkJXqoFa5Z2k5t7nt2qpucieEJQd + d5xkpApknC5ZWij3G8REKh1S78pnEeNvJY4UX2+r2q3GBWnfvktttyJ5+J/YtP4il4xsrTK3pN7G + m79aN+aVtZtnKa+Mbbdonr3GLh0QrbK2J4ros/ZKUrsbeILOyYm1mK15rAOOJ59uTeXa2fYfmxyH + ZKUGAAA= headers: content-encoding: [gzip] content-length: ['632'] @@ -50,22 +50,22 @@ interactions: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2Uomy8gvhh6SdpAqQO2wHv/bank_accounts?is_valid=True&limit=10&offset=0 + uri: https://api.balancedpayments.com:443/customers/CU3O2IKYocPZ5LA7GkZWpwKZ/bank_accounts?is_valid=True&limit=10&offset=0 response: body: string: !!binary | - H4sIAAAAAAAAA7VUXW/aMBR976+I8tCnFWKTBIKEJpi6VVtXtLXdtE5TZGwHLPJVx2EglP8+2yQk - oWTqS18icc/1veeec7n7C8MwFyhe+wjjJI9FZo6N3zJoGHv9lTBPcsHipR/n0YJyiZsAAsuGoxE0 - 31VJukaMIqrwn9e3t/fGx+n3T3NjNr37YtxN68yykS92qU7GK4rXsn6dUZW5SSLKjc9oV0Mhi9eK - YkVO0sN5JlSiqvXhET4m0W603KxW7j1Jp8/f5vDvzeZY4GRaf0M5CxhGgiWxLBDnYVhOVBxHwyj2 - MaeECZkheE5rhFMkKPGRQkxoAfvKGl5Z4AGA8QCOodNzXNv2Rk/1AIGclPKUs1i/IQi7DrCJR8gA - AUyGCHvQIc7Q9qg1sEYYSjxAcIgdy0U0WFjYW0DgAhAEnud6deE8Jf/l4vag6zmO3eCy4jRQJPqt - BejPpvDHcshnS/crf4rm3N3unq9/YVL3iqhAyoRao8rUeke22y0cDFDDd0I4zU7NY2JX6l6pKi2S - LlN4PgxehjMhB38ZThMJhD5OyBlQ7zrfNdGzvhO60LYHKMxq3xlRsnXqpCsV8vtHzWRWaumwGaJM - O9+v9jbrd21t25f3LPM3KGRk8iB38DJkERMTYF0mQZBRMbFKpc2YblUDtcoHSc2jz2/VUnORPYFV - djxwkpEqkHK6YUmu3G8QE4l0SL0rnwWMv5U4Uny9rWq3Ghekfft62m5F8vQ/sW/9RXqMFP0yt6Te - xpu/WjfmlbWbZymrjG23aJ69xi6dEK2yio4i+qy9ktThBnbQ6ZxYi9map3/CsfNpYSrXLop/NxcR - yKUGAAA= + H4sIAAAAAAAAA7VUXW/aMBR976+I8tCntbHzQQgSmujUsQ3UdlunbkxTZGyneIQkdRwKQvz32SYh + CQWtL32JxD3X9557zuVuzgzDnKJkHiKM0yIRudkzfsugYWz0V8I8LQRLHsOkWEwpl7gJbQhcu9u1 + zXdVkq6RoAVV+MP1ePzd+Dj4Nrw1rgY3I+NmUGeWjUKxznQynlE8l/XrjKrMp3RBufEFrWsoZslc + UazISXq4yIVKVLU+/HBu7c+jXym+m3jjgT+cTx6y59FkX+Bg2nBJOYsYRoKliSyQFHFcTrTdj4ZR + EmJOCRMyQ/CC1ginSFASIoWYNoDuBfAvALyHXs91em5wCfxO1/Pq/mYkJ6U84yzRbwjCHQ+6JCDE + QRATH+HA9ojnuwEFDuhiW+IRsn3sgQ6i0RTgYGrDDoRRFASdoFamyMh/uHjA90CTy4zTSJGwWgtg + XQ2cr+Mkvx4+8vzvUzT7OXOduyeX1L0WVCBlQq1RZWq9I6vVynYc1PCdEE7zQ/OYWJe6V6pKi6TL + 1D4ehi/DuZCDvwxnqQTiEKfkCKh3na+b6FHfCZ1q2yMU57XvjCjZTuqkK23l94+ayazU0mEzRrl2 + 3qr2NrdObW3bl/csD5coZqR/L3fwPGYLJvoQnKdRlFPRB6XSZkJXqoFa5Z2k5t7nt2qpucieEJQd + d5xkpApknC5ZWij3G8REKh1S78pnEeNvJY4UX2+r2q3GBWnfvktttyJ5+J/YtP4il4xsrTK3pN7G + m79aN+aVtZtnKa+Mbbdonr3GLh0QrbK2J4ros/ZKUrsbeILOyYm1mK15rAOOJ59uTeXa2fYfmxyH + ZKUGAAA= headers: content-encoding: [gzip] content-length: ['632'] @@ -75,66 +75,66 @@ interactions: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2Uomy8gvhh6SdpAqQO2wHv + uri: https://api.balancedpayments.com:443/customers/CU3O2IKYocPZ5LA7GkZWpwKZ response: body: string: !!binary | - H4sIAAAAAAAAA41Uy27bMBC85ysEnevIUoxY9S3opbeiaHJpUQg0yUREJFLhw6lh6N+7lCWZJtWw - Fx12Z4bc2RFPN0mSYqO0aKlU6S75BYUkOQ1faHHUUqhy0zSfplrD+KuFTiCAKWEk9oFQJ1RpxpFm - go8qo0g/q5mOIE1JhTQg0mKdb1br7WqdP+b57q7YFZvbsvy83RY/05mCJY1QtuXd5r50KETsq1Zw - XfvDMGKP/fJUPIn2WL4c6vr+B+ke3r5/K96/Hi5ndrXg/oBpLemzpWezg1lcCYzGNeK6UhppY41M - uVi1SOP6clxLNfIspm+DQWngoJ3tSJH0R0OESKq8TWGmjx4Q9gQrpcVyOQ/L9uK+FyDSCWg0FRZk - oYmF4Voe3W4wyN4ouIdS1VLqlOJVg5Te+GPSFrEmKLKrwPVw1m+bHzupE95L9G/nCKcZuDbkWWWn - ebFjvx9X5BAxkqSqRUOGVTpRcMiM9JmDOyfZ0dgj/lohPJgUkbmGBkqE7llMYsQscN2/9R8+OL/0 - ghn0j6aSQwr+b5gQHlzK2hax5AwJmISpzmgaIc+ogK8l4gph+3hFNK6QgQ48EobHZphAC+wDvMyo - iVxB0gkWKAhJzm/7B+EcMQEXnloSzdMEgv+rv+n/AqhHO/tTBgAA + H4sIAAAAAAAAA41UTY/TMBS876+IcqabpJu2orcVB4QWCQ4gRBGKXNsoVhM7+AOoqvx3ntMk69oB + 7yWH55mJ37zxu9wlSYqN0qKlUqX75BsUkuQyfOGIo5ZClZumeTXVGsZPFjqBAKaEkdgHQp1QpRlH + mgk+qowi/axmOoI0JRXSgEjXeVGu8t0qLz4Vm335sC+397tNmW/zQzpTsKQRynazLnavHQoRx6oV + XNd+M4zY3775/PBh/e7pq8AfD5v3j7u3p8OX7veTI9DVgvsNprWkPyw9mx3M4kpgNK4R15XSSBtr + ZMrFqkUa188ttlQjz2L6czAoDRy0vZ0pkn5riBBJlTcpzPTZA8KcYKR0vVwuwrK9uO8FiHQCDpoK + C7JwiIXhWp7d06CRo1FwD6WqpdQpxasGKV36bdIWsSYospvA9fCv7zY/tlMnvM/Rv58jnGbg2pBn + lV3mwY7n/Tgih4iRJFUtGjKM0omCQ2akzxzcNcmOxhHxU4XwYFJE5hYaKBF6ZDGJEbPAdV/rP3xw + nvSCGfSPppJDCl7WTAgPLmVti1hyhQRMwlRnNI2QZ1TA1xJxhbBdXhGNG2SgA0vC8FgPE2iB/Qs2 + M2oiV5B0ggUKQpLrbv9POEdMwIVVS6J5mkDwvvq7/i9xeE/EUwYAAA== headers: content-encoding: [gzip] - content-length: ['495'] + content-length: ['496'] content-type: [application/json] status: {code: 200, message: OK} - request: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2Uomy8gvhh6SdpAqQO2wHv + uri: https://api.balancedpayments.com:443/customers/CU3O2IKYocPZ5LA7GkZWpwKZ response: body: string: !!binary | - H4sIAAAAAAAAA41Uy27bMBC85ysEnevIUoxY9S3opbeiaHJpUQg0yUREJFLhw6lh6N+7lCWZJtWw - Fx12Z4bc2RFPN0mSYqO0aKlU6S75BYUkOQ1faHHUUqhy0zSfplrD+KuFTiCAKWEk9oFQJ1RpxpFm - go8qo0g/q5mOIE1JhTQg0mKdb1br7WqdP+b57q7YFZvbsvy83RY/05mCJY1QtuXd5r50KETsq1Zw - XfvDMGKP/fJUPIn2WL4c6vr+B+ke3r5/K96/Hi5ndrXg/oBpLemzpWezg1lcCYzGNeK6UhppY41M - uVi1SOP6clxLNfIspm+DQWngoJ3tSJH0R0OESKq8TWGmjx4Q9gQrpcVyOQ/L9uK+FyDSCWg0FRZk - oYmF4Voe3W4wyN4ouIdS1VLqlOJVg5Te+GPSFrEmKLKrwPVw1m+bHzupE95L9G/nCKcZuDbkWWWn - ebFjvx9X5BAxkqSqRUOGVTpRcMiM9JmDOyfZ0dgj/lohPJgUkbmGBkqE7llMYsQscN2/9R8+OL/0 - ghn0j6aSQwr+b5gQHlzK2hax5AwJmISpzmgaIc+ogK8l4gph+3hFNK6QgQ48EobHZphAC+wDvMyo - iVxB0gkWKAhJzm/7B+EcMQEXnloSzdMEgv+rv+n/AqhHO/tTBgAA + H4sIAAAAAAAAA41UTY/TMBS876+IcqabpJu2orcVB4QWCQ4gRBGKXNsoVhM7+AOoqvx3ntMk69oB + 7yWH55mJ37zxu9wlSYqN0qKlUqX75BsUkuQyfOGIo5ZClZumeTXVGsZPFjqBAKaEkdgHQp1QpRlH + mgk+qowi/axmOoI0JRXSgEjXeVGu8t0qLz4Vm335sC+397tNmW/zQzpTsKQRynazLnavHQoRx6oV + XNd+M4zY3775/PBh/e7pq8AfD5v3j7u3p8OX7veTI9DVgvsNprWkPyw9mx3M4kpgNK4R15XSSBtr + ZMrFqkUa188ttlQjz2L6czAoDRy0vZ0pkn5riBBJlTcpzPTZA8KcYKR0vVwuwrK9uO8FiHQCDpoK + C7JwiIXhWp7d06CRo1FwD6WqpdQpxasGKV36bdIWsSYospvA9fCv7zY/tlMnvM/Rv58jnGbg2pBn + lV3mwY7n/Tgih4iRJFUtGjKM0omCQ2akzxzcNcmOxhHxU4XwYFJE5hYaKBF6ZDGJEbPAdV/rP3xw + nvSCGfSPppJDCl7WTAgPLmVti1hyhQRMwlRnNI2QZ1TA1xJxhbBdXhGNG2SgA0vC8FgPE2iB/Qs2 + M2oiV5B0ggUKQpLrbv9POEdMwIVVS6J5mkDwvvq7/i9xeE/EUwYAAA== headers: content-encoding: [gzip] - content-length: ['495'] + content-length: ['496'] content-type: [application/json] status: {code: 200, message: OK} - request: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2Uomy8gvhh6SdpAqQO2wHv/bank_accounts?is_valid=True&limit=10&offset=0 + uri: https://api.balancedpayments.com:443/customers/CU3O2IKYocPZ5LA7GkZWpwKZ/bank_accounts?is_valid=True&limit=10&offset=0 response: body: string: !!binary | - H4sIAAAAAAAAA7VUXW/aMBR976+I8tCnFWKTBIKEJpi6VVtXtLXdtE5TZGwHLPJVx2EglP8+2yQk - oWTqS18icc/1veeec7n7C8MwFyhe+wjjJI9FZo6N3zJoGHv9lTBPcsHipR/n0YJyiZsAAsuGoxE0 - 31VJukaMIqrwn9e3t/fGx+n3T3NjNr37YtxN68yykS92qU7GK4rXsn6dUZW5SSLKjc9oV0Mhi9eK - YkVO0sN5JlSiqvXhET4m0W603KxW7j1Jp8/f5vDvzeZY4GRaf0M5CxhGgiWxLBDnYVhOVBxHwyj2 - MaeECZkheE5rhFMkKPGRQkxoAfvKGl5Z4AGA8QCOodNzXNv2Rk/1AIGclPKUs1i/IQi7DrCJR8gA - AUyGCHvQIc7Q9qg1sEYYSjxAcIgdy0U0WFjYW0DgAhAEnud6deE8Jf/l4vag6zmO3eCy4jRQJPqt - BejPpvDHcshnS/crf4rm3N3unq9/YVL3iqhAyoRao8rUeke22y0cDFDDd0I4zU7NY2JX6l6pKi2S - LlN4PgxehjMhB38ZThMJhD5OyBlQ7zrfNdGzvhO60LYHKMxq3xlRsnXqpCsV8vtHzWRWaumwGaJM - O9+v9jbrd21t25f3LPM3KGRk8iB38DJkERMTYF0mQZBRMbFKpc2YblUDtcoHSc2jz2/VUnORPYFV - djxwkpEqkHK6YUmu3G8QE4l0SL0rnwWMv5U4Uny9rWq3Ghekfft62m5F8vQ/sW/9RXqMFP0yt6Te - xpu/WjfmlbWbZymrjG23aJ69xi6dEK2yio4i+qy9ktThBnbQ6ZxYi9map3/CsfNpYSrXLop/NxcR - yKUGAAA= + H4sIAAAAAAAAA7VUXW/aMBR976+I8tCntbHzQQgSmujUsQ3UdlunbkxTZGyneIQkdRwKQvz32SYh + CQWtL32JxD3X9557zuVuzgzDnKJkHiKM0yIRudkzfsugYWz0V8I8LQRLHsOkWEwpl7gJbQhcu9u1 + zXdVkq6RoAVV+MP1ePzd+Dj4Nrw1rgY3I+NmUGeWjUKxznQynlE8l/XrjKrMp3RBufEFrWsoZslc + UazISXq4yIVKVLU+/HBu7c+jXym+m3jjgT+cTx6y59FkX+Bg2nBJOYsYRoKliSyQFHFcTrTdj4ZR + EmJOCRMyQ/CC1ginSFASIoWYNoDuBfAvALyHXs91em5wCfxO1/Pq/mYkJ6U84yzRbwjCHQ+6JCDE + QRATH+HA9ojnuwEFDuhiW+IRsn3sgQ6i0RTgYGrDDoRRFASdoFamyMh/uHjA90CTy4zTSJGwWgtg + XQ2cr+Mkvx4+8vzvUzT7OXOduyeX1L0WVCBlQq1RZWq9I6vVynYc1PCdEE7zQ/OYWJe6V6pKi6TL + 1D4ehi/DuZCDvwxnqQTiEKfkCKh3na+b6FHfCZ1q2yMU57XvjCjZTuqkK23l94+ayazU0mEzRrl2 + 3qr2NrdObW3bl/csD5coZqR/L3fwPGYLJvoQnKdRlFPRB6XSZkJXqoFa5Z2k5t7nt2qpucieEJQd + d5xkpApknC5ZWij3G8REKh1S78pnEeNvJY4UX2+r2q3GBWnfvktttyJ5+J/YtP4il4xsrTK3pN7G + m79aN+aVtZtnKa+Mbbdonr3GLh0QrbK2J4ros/ZKUrsbeILOyYm1mK15rAOOJ59uTeXa2fYfmxyH + ZKUGAAA= headers: content-encoding: [gzip] content-length: ['632'] @@ -144,22 +144,22 @@ interactions: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2Uomy8gvhh6SdpAqQO2wHv/bank_accounts?is_valid=True&limit=10&offset=0 + uri: https://api.balancedpayments.com:443/customers/CU3O2IKYocPZ5LA7GkZWpwKZ/bank_accounts?is_valid=True&limit=10&offset=0 response: body: string: !!binary | - H4sIAAAAAAAAA7VUXW/aMBR976+I8tCnFWKTBIKEJpi6VVtXtLXdtE5TZGwHLPJVx2EglP8+2yQk - oWTqS18icc/1veeec7n7C8MwFyhe+wjjJI9FZo6N3zJoGHv9lTBPcsHipR/n0YJyiZsAAsuGoxE0 - 31VJukaMIqrwn9e3t/fGx+n3T3NjNr37YtxN68yykS92qU7GK4rXsn6dUZW5SSLKjc9oV0Mhi9eK - YkVO0sN5JlSiqvXhET4m0W603KxW7j1Jp8/f5vDvzeZY4GRaf0M5CxhGgiWxLBDnYVhOVBxHwyj2 - MaeECZkheE5rhFMkKPGRQkxoAfvKGl5Z4AGA8QCOodNzXNv2Rk/1AIGclPKUs1i/IQi7DrCJR8gA - AUyGCHvQIc7Q9qg1sEYYSjxAcIgdy0U0WFjYW0DgAhAEnud6deE8Jf/l4vag6zmO3eCy4jRQJPqt - BejPpvDHcshnS/crf4rm3N3unq9/YVL3iqhAyoRao8rUeke22y0cDFDDd0I4zU7NY2JX6l6pKi2S - LlN4PgxehjMhB38ZThMJhD5OyBlQ7zrfNdGzvhO60LYHKMxq3xlRsnXqpCsV8vtHzWRWaumwGaJM - O9+v9jbrd21t25f3LPM3KGRk8iB38DJkERMTYF0mQZBRMbFKpc2YblUDtcoHSc2jz2/VUnORPYFV - djxwkpEqkHK6YUmu3G8QE4l0SL0rnwWMv5U4Uny9rWq3Ghekfft62m5F8vQ/sW/9RXqMFP0yt6Te - xpu/WjfmlbWbZymrjG23aJ69xi6dEK2yio4i+qy9ktThBnbQ6ZxYi9map3/CsfNpYSrXLop/NxcR - yKUGAAA= + H4sIAAAAAAAAA7VUXW/aMBR976+I8tCntbHzQQgSmujUsQ3UdlunbkxTZGyneIQkdRwKQvz32SYh + CQWtL32JxD3X9557zuVuzgzDnKJkHiKM0yIRudkzfsugYWz0V8I8LQRLHsOkWEwpl7gJbQhcu9u1 + zXdVkq6RoAVV+MP1ePzd+Dj4Nrw1rgY3I+NmUGeWjUKxznQynlE8l/XrjKrMp3RBufEFrWsoZslc + UazISXq4yIVKVLU+/HBu7c+jXym+m3jjgT+cTx6y59FkX+Bg2nBJOYsYRoKliSyQFHFcTrTdj4ZR + EmJOCRMyQ/CC1ginSFASIoWYNoDuBfAvALyHXs91em5wCfxO1/Pq/mYkJ6U84yzRbwjCHQ+6JCDE + QRATH+HA9ojnuwEFDuhiW+IRsn3sgQ6i0RTgYGrDDoRRFASdoFamyMh/uHjA90CTy4zTSJGwWgtg + XQ2cr+Mkvx4+8vzvUzT7OXOduyeX1L0WVCBlQq1RZWq9I6vVynYc1PCdEE7zQ/OYWJe6V6pKi6TL + 1D4ehi/DuZCDvwxnqQTiEKfkCKh3na+b6FHfCZ1q2yMU57XvjCjZTuqkK23l94+ayazU0mEzRrl2 + 3qr2NrdObW3bl/csD5coZqR/L3fwPGYLJvoQnKdRlFPRB6XSZkJXqoFa5Z2k5t7nt2qpucieEJQd + d5xkpApknC5ZWij3G8REKh1S78pnEeNvJY4UX2+r2q3GBWnfvktttyJ5+J/YtP4il4xsrTK3pN7G + m79aN+aVtZtnKa+Mbbdonr3GLh0QrbK2J4ros/ZKUrsbeILOyYm1mK15rAOOJ59uTeXa2fYfmxyH + ZKUGAAA= headers: content-encoding: [gzip] content-length: ['632'] diff --git a/tests/py/fixtures/TestBalancedCard.yml b/tests/py/fixtures/TestBalancedCard.yml index 9a84ae73af..1e6607ec3d 100644 --- a/tests/py/fixtures/TestBalancedCard.yml +++ b/tests/py/fixtures/TestBalancedCard.yml @@ -3,250 +3,250 @@ interactions: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2RCigJCqQDdYQEcThv3Jxr + uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd response: body: string: !!binary | - H4sIAAAAAAAAA41Uy27bMBC85ysEnevIkp028NXtJbcU6aEtCmFNshURiXT4CGIY+vcubUmmSaXs - RYfdmSF3dsTjTZblxGojO6Z0vsl+YiHLjqcvtgR0DKvCtu2HsdZy8eygIwhhWlpFQiDWKdOGCzBc - ikFlEOknNbunYBitwSAir5blerH8tFiWT2W5WVWbqrpd35X398sf+UQhiiUoq2p993HlUajc1Z0U - pgmH4dQdu/1Wfd3yPw/bl8fP9PvjF/LUvK4e3tTlzH0jRThg3ij229GLycEirYRGkwaEqbUBY52R - uZCLDgxpLsd1zEBgMXtx0DKPHHSzHRiocDSgVDEdbIpwcwiAuCdcKavmy2VcdhcPvUCRvcRGWxNJ - Z5pEWmHUwe9Gg+ysxntoXc+lTmtRt6DNOhyTdcDbqMivAtfjWb9cftykXngv0b+dIpwX6Nopz7o4 - Tosd+v2wIo9IQNG6kS09rdKLgkfmtC883DnJnsYOxHMN5GRSQuYaGilRtuMpiQEzw/X/1nd88H7p - GTPYm2FKYAr+b5gYHl3K2Zaw5AyJmJTrvTUsQZ5QEd8oEBqIe7wSGlfISAcfCStSM4ygGfYrvszQ - Jq6g2AiLFKSi57f9H+EcMBEXn1qazNMIwv+rv+n/AoxtLhJTBgAA + H4sIAAAAAAAAA41Uy27bMBC85ysEnWvLipQ29i1oby2CAG3RokUh0CQLEZFIhY+ggqF/79KWZJpU + wlx02J0ZcmdHPFwlSYqN0qKlUqW75DcUkuRw/EKLo5ZClZumeTfVGsYfLXQCAUwJI7EPhDqhSjOO + NBN8VBlFhlnNdARpSiqkAZFeb/Jytfmw2uTf8ptdWezKcp0Xxfvb7a90pmBJX6UU622+uSlKh0LE + vmoF17U/DCP22I/fi88/Sf98//UT7b88FN3dD7mtn8j5zK4W3B8wrSX9a+nZ7GAWVwKjcY24rpRG + 2lgjUy5WLdK4Ph/XUo08i+mTheZp4KCdradI+qMhQiRV3qYw070HhD3BSun1cjkPy/bivhcg0glo + NBUWZKGJheFa9m43GGRvFNxDqWopdUrxqkFKl/6YtEWsCYrsInADnPXH5sdO6oT3HP31HOE0A9eO + eVbZYV7s2B/GFTlEjCSpatGQ4yqdKDhkRobMwZ2S7GjsEX+sED6aFJG5hAZKhO5ZTGLELHDdv/UF + H5xfesEM+k9TySEFbxsmhAeXsrZFLDlBAiZhqjOaRsgzKuBribhC2D5eEY0LZKADj4ThsRkm0AL7 + GV5m1ESuIOkECxSEJKe3/ZVwjpiAC08tieZpAsH/NVwN/wHQiK4AUwYAAA== headers: content-encoding: [gzip] - content-length: ['495'] + content-length: ['499'] content-type: [application/json] status: {code: 200, message: OK} - request: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2RCigJCqQDdYQEcThv3Jxr/cards?is_valid=True&limit=10&offset=0 + uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd/cards?is_valid=True&limit=10&offset=0 response: body: string: !!binary | - H4sIAAAAAAAAA61VWU/jMBB+51dYeeAJ2sQpvSS0KgWxAlHulZbVKnJ9tBapXWyn2wr1v6/tJE3C - od0H/BApc3hmvvlm/LoHQICRIjoYgl/2B4BX/7XilItnJy4FzjLTRi6ostJg/Ajvxnx2MX65PSU/ - b8/ww3wVX6xVUPhvD8qLGBczqpaKC+Mc+7jHwg6M4+4RYv0+jmncxSyEvU6XYtTtMIJZ3CUEQxqx - eGDNKR6wmDESHiHY7QW7i/FqlSiqs9TdK7I03WlEtpjmaa5rJ7Kn8kYrnSylNihNFsjguctNyEpP - 10uukOFSJAspjNNH4S7CghrUBEfRmbX10EjBMu1+SnOHHTebxMg/3qKuQITYInQCneJErgF8DyEn - /t4xvD+/OZ3cnDyG8vpyiqgemJdxrSaMDJ1JtXHW0swtBLuEzWZJnRgrSrhpgljW38BwisRzItDC - e13cXF3fnY8mYPx9dH8GTkaTywMwaY1aTTy1UZSaCs8N1ZXBVCHh6/jBNarE2ZLYrEmCPDtgGHUO - w95hGD1E0TCGQxi3+v0+7AyeaqFyyKx9nZwWYCup15CzmDpkPxBHLl4EY3CFuAD3PvV6Yyw1jC/+ - w34W1MGSeJtBCKOw7o1lJozaJIWBi/9uMjASCaFT244hMCqjFX9z2Bv9qPFxQ5GbQRjCipB2GN6U - GXCdrKjijFMHeyOAI/9udIK8dlAwEdg2gbw84LIHRAIhDfAsqTXcZV+waQgYSnWV/1xR5lBp+93S - /g/iKvoPEsB+pxP3nvLZ2Foofzu0gsYcBinSnkXtclPZ2J/sqTy1bw4jlHJy/GDx30/5gpvjKNyX - jGlqjsuOBoKum0smqEosluJXhfI52CLKZRPkuVhJ0exgqeiKy8wt54ohgZF2lTm/nEUB4+qrwbCA - +7X+5m3IX5AW4XqZGTvxVeNfff9bnGzbO22RXq6pvyi1phV+pXZbDFYRyLkmc5n6V6vkWC1UTd8I - 5kftE59C5yrc2/4F27WBRxcHAAA= + H4sIAAAAAAAAA61VW0/bMBR+51dYfuAJ2lza9CKhqbBpaEBBg8HENEVubFOL1M5spzRC/e+znaRJ + uGh7wA+Rci4+53znO8fPewDABEms4BT8Mj8APLuvEaeMP1pxLbCWudJiRaSRwpMf4dlPXKzn159J + cX4VZrM7OVn+wbDy3x7UF1HGH4jMJOPaOo6TEfUGQRhGQ0TH4yQkYZRQLxgNIpKgaEBxQsMI4yQg + Pg0nxpwkExpSir0hCqIR3F2crNexJCpP7b08T9OdhuerRZnmpnV8cxpvtFZxJpRGabxCOlna3Lho + 9GSTMYk0EzxeCa6t3vd2EVZEoy44kjwYWweN4DRX9qc2t9gxXcRaPDmLtgJhbIpQcWAVx2IDgtcQ + MuzuPQkvTo/VXbQ+Reehys9v8c3t4nLSQgRp8iBkYa2FXhoIdgnrIiNWnEiCme6CWNffwXCB+GPM + 0cp5fbu6uPz+dTYHJ6ez6y/geDY/OwDz3qzXxVNpSYhu8CyIagwWEnFXxy1TqBHnGTZZ4xg5dgSe + Pzj0Roeef+MPp4NwOhj2xkM/CP37VqgSMmPfJqcB2EjaNZQsJhbZN8S+jWduBheIcXDtUm83xlBD + u+Lf7GdFnURgZzPxAt9reyci51oWcWVg47+ajATxGJOFaccUaJmThr8l7J1+tPhYEGRnMPCChpBm + GF6UCZmK10QyyoiFvRPAkn83OrCsHVRMBKZNoCwP2OwBFoALDRxLWg232VdsmgKKUtXkv5SEWlT6 + brf0/4O4kvyDBIMonPjhfTkbWwPlb4sW7MwhTJFyLOrXm8rEfmdPlal9shihlOGjG4P/fspWTB/5 + 3r6gVBF9VHcUcrLpLhnYlFgtxY8K5XIwRdTLBpa5GEnVbJhJsmYit8u5YQjUwqwy61eyCFImPxoM + A7hb6y/ehvIF6WGmslybiW8a/+z632N4299pq/RKTftFaTWt8qu122qwqkDWNV6K1L1aNcdaoVr6 + TjA3au/4VDpb4d72LxntgTEXBwAA headers: content-encoding: [gzip] - content-length: ['761'] + content-length: ['762'] content-type: [application/json] status: {code: 200, message: OK} - request: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2RCigJCqQDdYQEcThv3Jxr/cards?is_valid=True&limit=10&offset=0 + uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd/cards?is_valid=True&limit=10&offset=0 response: body: string: !!binary | - H4sIAAAAAAAAA61VWU/jMBB+51dYeeAJ2sQpvSS0KgWxAlHulZbVKnJ9tBapXWyn2wr1v6/tJE3C - od0H/BApc3hmvvlm/LoHQICRIjoYgl/2B4BX/7XilItnJy4FzjLTRi6ostJg/Ajvxnx2MX65PSU/ - b8/ww3wVX6xVUPhvD8qLGBczqpaKC+Mc+7jHwg6M4+4RYv0+jmncxSyEvU6XYtTtMIJZ3CUEQxqx - eGDNKR6wmDESHiHY7QW7i/FqlSiqs9TdK7I03WlEtpjmaa5rJ7Kn8kYrnSylNihNFsjguctNyEpP - 10uukOFSJAspjNNH4S7CghrUBEfRmbX10EjBMu1+SnOHHTebxMg/3qKuQITYInQCneJErgF8DyEn - /t4xvD+/OZ3cnDyG8vpyiqgemJdxrSaMDJ1JtXHW0swtBLuEzWZJnRgrSrhpgljW38BwisRzItDC - e13cXF3fnY8mYPx9dH8GTkaTywMwaY1aTTy1UZSaCs8N1ZXBVCHh6/jBNarE2ZLYrEmCPDtgGHUO - w95hGD1E0TCGQxi3+v0+7AyeaqFyyKx9nZwWYCup15CzmDpkPxBHLl4EY3CFuAD3PvV6Yyw1jC/+ - w34W1MGSeJtBCKOw7o1lJozaJIWBi/9uMjASCaFT244hMCqjFX9z2Bv9qPFxQ5GbQRjCipB2GN6U - GXCdrKjijFMHeyOAI/9udIK8dlAwEdg2gbw84LIHRAIhDfAsqTXcZV+waQgYSnWV/1xR5lBp+93S - /g/iKvoPEsB+pxP3nvLZ2Foofzu0gsYcBinSnkXtclPZ2J/sqTy1bw4jlHJy/GDx30/5gpvjKNyX - jGlqjsuOBoKum0smqEosluJXhfI52CLKZRPkuVhJ0exgqeiKy8wt54ohgZF2lTm/nEUB4+qrwbCA - +7X+5m3IX5AW4XqZGTvxVeNfff9bnGzbO22RXq6pvyi1phV+pXZbDFYRyLkmc5n6V6vkWC1UTd8I - 5kftE59C5yrc2/4F27WBRxcHAAA= + H4sIAAAAAAAAA61VW0/bMBR+51dYfuAJ2lza9CKhqbBpaEBBg8HENEVubFOL1M5spzRC/e+znaRJ + uGh7wA+Rci4+53znO8fPewDABEms4BT8Mj8APLuvEaeMP1pxLbCWudJiRaSRwpMf4dlPXKzn159J + cX4VZrM7OVn+wbDy3x7UF1HGH4jMJOPaOo6TEfUGQRhGQ0TH4yQkYZRQLxgNIpKgaEBxQsMI4yQg + Pg0nxpwkExpSir0hCqIR3F2crNexJCpP7b08T9OdhuerRZnmpnV8cxpvtFZxJpRGabxCOlna3Lho + 9GSTMYk0EzxeCa6t3vd2EVZEoy44kjwYWweN4DRX9qc2t9gxXcRaPDmLtgJhbIpQcWAVx2IDgtcQ + MuzuPQkvTo/VXbQ+Reehys9v8c3t4nLSQgRp8iBkYa2FXhoIdgnrIiNWnEiCme6CWNffwXCB+GPM + 0cp5fbu6uPz+dTYHJ6ez6y/geDY/OwDz3qzXxVNpSYhu8CyIagwWEnFXxy1TqBHnGTZZ4xg5dgSe + Pzj0Roeef+MPp4NwOhj2xkM/CP37VqgSMmPfJqcB2EjaNZQsJhbZN8S+jWduBheIcXDtUm83xlBD + u+Lf7GdFnURgZzPxAt9reyci51oWcWVg47+ajATxGJOFaccUaJmThr8l7J1+tPhYEGRnMPCChpBm + GF6UCZmK10QyyoiFvRPAkn83OrCsHVRMBKZNoCwP2OwBFoALDRxLWg232VdsmgKKUtXkv5SEWlT6 + brf0/4O4kvyDBIMonPjhfTkbWwPlb4sW7MwhTJFyLOrXm8rEfmdPlal9shihlOGjG4P/fspWTB/5 + 3r6gVBF9VHcUcrLpLhnYlFgtxY8K5XIwRdTLBpa5GEnVbJhJsmYit8u5YQjUwqwy61eyCFImPxoM + A7hb6y/ehvIF6WGmslybiW8a/+z632N4299pq/RKTftFaTWt8qu122qwqkDWNV6K1L1aNcdaoVr6 + TjA3au/4VDpb4d72LxntgTEXBwAA headers: content-encoding: [gzip] - content-length: ['761'] + content-length: ['762'] content-type: [application/json] status: {code: 200, message: OK} - request: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2RCigJCqQDdYQEcThv3Jxr + uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd response: body: string: !!binary | - H4sIAAAAAAAAA41Uy27bMBC85ysEnevIkp028NXtJbcU6aEtCmFNshURiXT4CGIY+vcubUmmSaXs - RYfdmSF3dsTjTZblxGojO6Z0vsl+YiHLjqcvtgR0DKvCtu2HsdZy8eygIwhhWlpFQiDWKdOGCzBc - ikFlEOknNbunYBitwSAir5blerH8tFiWT2W5WVWbqrpd35X398sf+UQhiiUoq2p993HlUajc1Z0U - pgmH4dQdu/1Wfd3yPw/bl8fP9PvjF/LUvK4e3tTlzH0jRThg3ij229GLycEirYRGkwaEqbUBY52R - uZCLDgxpLsd1zEBgMXtx0DKPHHSzHRiocDSgVDEdbIpwcwiAuCdcKavmy2VcdhcPvUCRvcRGWxNJ - Z5pEWmHUwe9Gg+ysxntoXc+lTmtRt6DNOhyTdcDbqMivAtfjWb9cftykXngv0b+dIpwX6Nopz7o4 - Tosd+v2wIo9IQNG6kS09rdKLgkfmtC883DnJnsYOxHMN5GRSQuYaGilRtuMpiQEzw/X/1nd88H7p - GTPYm2FKYAr+b5gYHl3K2Zaw5AyJmJTrvTUsQZ5QEd8oEBqIe7wSGlfISAcfCStSM4ygGfYrvszQ - Jq6g2AiLFKSi57f9H+EcMBEXn1qazNMIwv+rv+n/AoxtLhJTBgAA + H4sIAAAAAAAAA41Uy27bMBC85ysEnWvLipQ29i1oby2CAG3RokUh0CQLEZFIhY+ggqF/79KWZJpU + wlx02J0ZcmdHPFwlSYqN0qKlUqW75DcUkuRw/EKLo5ZClZumeTfVGsYfLXQCAUwJI7EPhDqhSjOO + NBN8VBlFhlnNdARpSiqkAZFeb/Jytfmw2uTf8ptdWezKcp0Xxfvb7a90pmBJX6UU622+uSlKh0LE + vmoF17U/DCP22I/fi88/Sf98//UT7b88FN3dD7mtn8j5zK4W3B8wrSX9a+nZ7GAWVwKjcY24rpRG + 2lgjUy5WLdK4Ph/XUo08i+mTheZp4KCdradI+qMhQiRV3qYw070HhD3BSun1cjkPy/bivhcg0glo + NBUWZKGJheFa9m43GGRvFNxDqWopdUrxqkFKl/6YtEWsCYrsInADnPXH5sdO6oT3HP31HOE0A9eO + eVbZYV7s2B/GFTlEjCSpatGQ4yqdKDhkRobMwZ2S7GjsEX+sED6aFJG5hAZKhO5ZTGLELHDdv/UF + H5xfesEM+k9TySEFbxsmhAeXsrZFLDlBAiZhqjOaRsgzKuBribhC2D5eEY0LZKADj4ThsRkm0AL7 + GV5m1ESuIOkECxSEJKe3/ZVwjpiAC08tieZpAsH/NVwN/wHQiK4AUwYAAA== headers: content-encoding: [gzip] - content-length: ['495'] + content-length: ['499'] content-type: [application/json] status: {code: 200, message: OK} - request: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/v1/marketplaces/TEST-MP1wTyxDAJKCaWPjBS4Oli4d/accounts/CU2RCigJCqQDdYQEcThv3Jxr + uri: https://api.balancedpayments.com:443/v1/marketplaces/TEST-MP1wTyxDAJKCaWPjBS4Oli4d/accounts/CU3KXdyvNSDeyLP3pAWr9hqd response: body: string: !!binary | - H4sIAAAAAAAAA41Uy27bMBC85ysEnevIkp028NXtJbcU6aEtCmFNshURiXT4CGIY+vcubUmmSaXs - RYfdmSF3dsTjTZblxGojO6Z0vsl+YiHLjqcvtgR0DKvCtu2HsdZy8eygIwhhWlpFQiDWKdOGCzBc - ikFlEOknNbunYBitwSAir5blerH8tFiWT2W5WVWbqrpd35X398sf+UQhiiUoq2p993HlUajc1Z0U - pgmH4dQdu/1Wfd3yPw/bl8fP9PvjF/LUvK4e3tTlzH0jRThg3ij229GLycEirYRGkwaEqbUBY52R - uZCLDgxpLsd1zEBgMXtx0DKPHHSzHRiocDSgVDEdbIpwcwiAuCdcKavmy2VcdhcPvUCRvcRGWxNJ - Z5pEWmHUwe9Gg+ysxntoXc+lTmtRt6DNOhyTdcDbqMivAtfjWb9cftykXngv0b+dIpwX6Nopz7o4 - Tosd+v2wIo9IQNG6kS09rdKLgkfmtC883DnJnsYOxHMN5GRSQuYaGilRtuMpiQEzw/X/1nd88H7p - GTPYm2FKYAr+b5gYHl3K2Zaw5AyJmJTrvTUsQZ5QEd8oEBqIe7wSGlfISAcfCStSM4ygGfYrvszQ - Jq6g2AiLFKSi57f9H+EcMBEXn1qazNMIwv+rv+n/AoxtLhJTBgAA + H4sIAAAAAAAAA41Uy27bMBC85ysEnWvLipQ29i1oby2CAG3RokUh0CQLEZFIhY+ggqF/79KWZJpU + wlx02J0ZcmdHPFwlSYqN0qKlUqW75DcUkuRw/EKLo5ZClZumeTfVGsYfLXQCAUwJI7EPhDqhSjOO + NBN8VBlFhlnNdARpSiqkAZFeb/Jytfmw2uTf8ptdWezKcp0Xxfvb7a90pmBJX6UU622+uSlKh0LE + vmoF17U/DCP22I/fi88/Sf98//UT7b88FN3dD7mtn8j5zK4W3B8wrSX9a+nZ7GAWVwKjcY24rpRG + 2lgjUy5WLdK4Ph/XUo08i+mTheZp4KCdradI+qMhQiRV3qYw070HhD3BSun1cjkPy/bivhcg0glo + NBUWZKGJheFa9m43GGRvFNxDqWopdUrxqkFKl/6YtEWsCYrsInADnPXH5sdO6oT3HP31HOE0A9eO + eVbZYV7s2B/GFTlEjCSpatGQ4yqdKDhkRobMwZ2S7GjsEX+sED6aFJG5hAZKhO5ZTGLELHDdv/UF + H5xfesEM+k9TySEFbxsmhAeXsrZFLDlBAiZhqjOaRsgzKuBribhC2D5eEY0LZKADj4ThsRkm0AL7 + GV5m1ESuIOkECxSEJKe3/ZVwjpiAC08tieZpAsH/NVwN/wHQiK4AUwYAAA== headers: content-encoding: [gzip] - content-length: ['495'] + content-length: ['499'] content-type: [application/json] status: {code: 200, message: OK} - request: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2RCigJCqQDdYQEcThv3Jxr/cards?is_valid=True&limit=10&offset=0 + uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd/cards?is_valid=True&limit=10&offset=0 response: body: string: !!binary | - H4sIAAAAAAAAA61VWU/jMBB+51dYeeAJ2sQpvSS0KgWxAlHulZbVKnJ9tBapXWyn2wr1v6/tJE3C - od0H/BApc3hmvvlm/LoHQICRIjoYgl/2B4BX/7XilItnJy4FzjLTRi6ostJg/Ajvxnx2MX65PSU/ - b8/ww3wVX6xVUPhvD8qLGBczqpaKC+Mc+7jHwg6M4+4RYv0+jmncxSyEvU6XYtTtMIJZ3CUEQxqx - eGDNKR6wmDESHiHY7QW7i/FqlSiqs9TdK7I03WlEtpjmaa5rJ7Kn8kYrnSylNihNFsjguctNyEpP - 10uukOFSJAspjNNH4S7CghrUBEfRmbX10EjBMu1+SnOHHTebxMg/3qKuQITYInQCneJErgF8DyEn - /t4xvD+/OZ3cnDyG8vpyiqgemJdxrSaMDJ1JtXHW0swtBLuEzWZJnRgrSrhpgljW38BwisRzItDC - e13cXF3fnY8mYPx9dH8GTkaTywMwaY1aTTy1UZSaCs8N1ZXBVCHh6/jBNarE2ZLYrEmCPDtgGHUO - w95hGD1E0TCGQxi3+v0+7AyeaqFyyKx9nZwWYCup15CzmDpkPxBHLl4EY3CFuAD3PvV6Yyw1jC/+ - w34W1MGSeJtBCKOw7o1lJozaJIWBi/9uMjASCaFT244hMCqjFX9z2Bv9qPFxQ5GbQRjCipB2GN6U - GXCdrKjijFMHeyOAI/9udIK8dlAwEdg2gbw84LIHRAIhDfAsqTXcZV+waQgYSnWV/1xR5lBp+93S - /g/iKvoPEsB+pxP3nvLZ2Foofzu0gsYcBinSnkXtclPZ2J/sqTy1bw4jlHJy/GDx30/5gpvjKNyX - jGlqjsuOBoKum0smqEosluJXhfI52CLKZRPkuVhJ0exgqeiKy8wt54ohgZF2lTm/nEUB4+qrwbCA - +7X+5m3IX5AW4XqZGTvxVeNfff9bnGzbO22RXq6pvyi1phV+pXZbDFYRyLkmc5n6V6vkWC1UTd8I - 5kftE59C5yrc2/4F27WBRxcHAAA= + H4sIAAAAAAAAA61VW0/bMBR+51dYfuAJ2lza9CKhqbBpaEBBg8HENEVubFOL1M5spzRC/e+znaRJ + uGh7wA+Rci4+53znO8fPewDABEms4BT8Mj8APLuvEaeMP1pxLbCWudJiRaSRwpMf4dlPXKzn159J + cX4VZrM7OVn+wbDy3x7UF1HGH4jMJOPaOo6TEfUGQRhGQ0TH4yQkYZRQLxgNIpKgaEBxQsMI4yQg + Pg0nxpwkExpSir0hCqIR3F2crNexJCpP7b08T9OdhuerRZnmpnV8cxpvtFZxJpRGabxCOlna3Lho + 9GSTMYk0EzxeCa6t3vd2EVZEoy44kjwYWweN4DRX9qc2t9gxXcRaPDmLtgJhbIpQcWAVx2IDgtcQ + MuzuPQkvTo/VXbQ+Reehys9v8c3t4nLSQgRp8iBkYa2FXhoIdgnrIiNWnEiCme6CWNffwXCB+GPM + 0cp5fbu6uPz+dTYHJ6ez6y/geDY/OwDz3qzXxVNpSYhu8CyIagwWEnFXxy1TqBHnGTZZ4xg5dgSe + Pzj0Roeef+MPp4NwOhj2xkM/CP37VqgSMmPfJqcB2EjaNZQsJhbZN8S+jWduBheIcXDtUm83xlBD + u+Lf7GdFnURgZzPxAt9reyci51oWcWVg47+ajATxGJOFaccUaJmThr8l7J1+tPhYEGRnMPCChpBm + GF6UCZmK10QyyoiFvRPAkn83OrCsHVRMBKZNoCwP2OwBFoALDRxLWg232VdsmgKKUtXkv5SEWlT6 + brf0/4O4kvyDBIMonPjhfTkbWwPlb4sW7MwhTJFyLOrXm8rEfmdPlal9shihlOGjG4P/fspWTB/5 + 3r6gVBF9VHcUcrLpLhnYlFgtxY8K5XIwRdTLBpa5GEnVbJhJsmYit8u5YQjUwqwy61eyCFImPxoM + A7hb6y/ehvIF6WGmslybiW8a/+z632N4299pq/RKTftFaTWt8qu122qwqkDWNV6K1L1aNcdaoVr6 + TjA3au/4VDpb4d72LxntgTEXBwAA headers: content-encoding: [gzip] - content-length: ['761'] + content-length: ['762'] content-type: [application/json] status: {code: 200, message: OK} - request: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2RCigJCqQDdYQEcThv3Jxr/cards?is_valid=True&limit=10&offset=0 + uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd/cards?is_valid=True&limit=10&offset=0 response: body: string: !!binary | - H4sIAAAAAAAAA61VWU/jMBB+51dYeeAJ2sQpvSS0KgWxAlHulZbVKnJ9tBapXWyn2wr1v6/tJE3C - od0H/BApc3hmvvlm/LoHQICRIjoYgl/2B4BX/7XilItnJy4FzjLTRi6ostJg/Ajvxnx2MX65PSU/ - b8/ww3wVX6xVUPhvD8qLGBczqpaKC+Mc+7jHwg6M4+4RYv0+jmncxSyEvU6XYtTtMIJZ3CUEQxqx - eGDNKR6wmDESHiHY7QW7i/FqlSiqs9TdK7I03WlEtpjmaa5rJ7Kn8kYrnSylNihNFsjguctNyEpP - 10uukOFSJAspjNNH4S7CghrUBEfRmbX10EjBMu1+SnOHHTebxMg/3qKuQITYInQCneJErgF8DyEn - /t4xvD+/OZ3cnDyG8vpyiqgemJdxrSaMDJ1JtXHW0swtBLuEzWZJnRgrSrhpgljW38BwisRzItDC - e13cXF3fnY8mYPx9dH8GTkaTywMwaY1aTTy1UZSaCs8N1ZXBVCHh6/jBNarE2ZLYrEmCPDtgGHUO - w95hGD1E0TCGQxi3+v0+7AyeaqFyyKx9nZwWYCup15CzmDpkPxBHLl4EY3CFuAD3PvV6Yyw1jC/+ - w34W1MGSeJtBCKOw7o1lJozaJIWBi/9uMjASCaFT244hMCqjFX9z2Bv9qPFxQ5GbQRjCipB2GN6U - GXCdrKjijFMHeyOAI/9udIK8dlAwEdg2gbw84LIHRAIhDfAsqTXcZV+waQgYSnWV/1xR5lBp+93S - /g/iKvoPEsB+pxP3nvLZ2Foofzu0gsYcBinSnkXtclPZ2J/sqTy1bw4jlHJy/GDx30/5gpvjKNyX - jGlqjsuOBoKum0smqEosluJXhfI52CLKZRPkuVhJ0exgqeiKy8wt54ohgZF2lTm/nEUB4+qrwbCA - +7X+5m3IX5AW4XqZGTvxVeNfff9bnGzbO22RXq6pvyi1phV+pXZbDFYRyLkmc5n6V6vkWC1UTd8I - 5kftE59C5yrc2/4F27WBRxcHAAA= + H4sIAAAAAAAAA61VW0/bMBR+51dYfuAJ2lza9CKhqbBpaEBBg8HENEVubFOL1M5spzRC/e+znaRJ + uGh7wA+Rci4+53znO8fPewDABEms4BT8Mj8APLuvEaeMP1pxLbCWudJiRaSRwpMf4dlPXKzn159J + cX4VZrM7OVn+wbDy3x7UF1HGH4jMJOPaOo6TEfUGQRhGQ0TH4yQkYZRQLxgNIpKgaEBxQsMI4yQg + Pg0nxpwkExpSir0hCqIR3F2crNexJCpP7b08T9OdhuerRZnmpnV8cxpvtFZxJpRGabxCOlna3Lho + 9GSTMYk0EzxeCa6t3vd2EVZEoy44kjwYWweN4DRX9qc2t9gxXcRaPDmLtgJhbIpQcWAVx2IDgtcQ + MuzuPQkvTo/VXbQ+Reehys9v8c3t4nLSQgRp8iBkYa2FXhoIdgnrIiNWnEiCme6CWNffwXCB+GPM + 0cp5fbu6uPz+dTYHJ6ez6y/geDY/OwDz3qzXxVNpSYhu8CyIagwWEnFXxy1TqBHnGTZZ4xg5dgSe + Pzj0Roeef+MPp4NwOhj2xkM/CP37VqgSMmPfJqcB2EjaNZQsJhbZN8S+jWduBheIcXDtUm83xlBD + u+Lf7GdFnURgZzPxAt9reyci51oWcWVg47+ajATxGJOFaccUaJmThr8l7J1+tPhYEGRnMPCChpBm + GF6UCZmK10QyyoiFvRPAkn83OrCsHVRMBKZNoCwP2OwBFoALDRxLWg232VdsmgKKUtXkv5SEWlT6 + brf0/4O4kvyDBIMonPjhfTkbWwPlb4sW7MwhTJFyLOrXm8rEfmdPlal9shihlOGjG4P/fspWTB/5 + 3r6gVBF9VHcUcrLpLhnYlFgtxY8K5XIwRdTLBpa5GEnVbJhJsmYit8u5YQjUwqwy61eyCFImPxoM + A7hb6y/ehvIF6WGmslybiW8a/+z632N4299pq/RKTftFaTWt8qu122qwqkDWNV6K1L1aNcdaoVr6 + TjA3au/4VDpb4d72LxntgTEXBwAA headers: content-encoding: [gzip] - content-length: ['761'] + content-length: ['762'] content-type: [application/json] status: {code: 200, message: OK} - request: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2RCigJCqQDdYQEcThv3Jxr + uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd response: body: string: !!binary | - H4sIAAAAAAAAA41Uy27bMBC85ysEnevIkp028NXtJbcU6aEtCmFNshURiXT4CGIY+vcubUmmSaXs - RYfdmSF3dsTjTZblxGojO6Z0vsl+YiHLjqcvtgR0DKvCtu2HsdZy8eygIwhhWlpFQiDWKdOGCzBc - ikFlEOknNbunYBitwSAir5blerH8tFiWT2W5WVWbqrpd35X398sf+UQhiiUoq2p993HlUajc1Z0U - pgmH4dQdu/1Wfd3yPw/bl8fP9PvjF/LUvK4e3tTlzH0jRThg3ij229GLycEirYRGkwaEqbUBY52R - uZCLDgxpLsd1zEBgMXtx0DKPHHSzHRiocDSgVDEdbIpwcwiAuCdcKavmy2VcdhcPvUCRvcRGWxNJ - Z5pEWmHUwe9Gg+ysxntoXc+lTmtRt6DNOhyTdcDbqMivAtfjWb9cftykXngv0b+dIpwX6Nopz7o4 - Tosd+v2wIo9IQNG6kS09rdKLgkfmtC883DnJnsYOxHMN5GRSQuYaGilRtuMpiQEzw/X/1nd88H7p - GTPYm2FKYAr+b5gYHl3K2Zaw5AyJmJTrvTUsQZ5QEd8oEBqIe7wSGlfISAcfCStSM4ygGfYrvszQ - Jq6g2AiLFKSi57f9H+EcMBEXn1qazNMIwv+rv+n/AoxtLhJTBgAA + H4sIAAAAAAAAA41Uy27bMBC85ysEnWvLipQ29i1oby2CAG3RokUh0CQLEZFIhY+ggqF/79KWZJpU + wlx02J0ZcmdHPFwlSYqN0qKlUqW75DcUkuRw/EKLo5ZClZumeTfVGsYfLXQCAUwJI7EPhDqhSjOO + NBN8VBlFhlnNdARpSiqkAZFeb/Jytfmw2uTf8ptdWezKcp0Xxfvb7a90pmBJX6UU622+uSlKh0LE + vmoF17U/DCP22I/fi88/Sf98//UT7b88FN3dD7mtn8j5zK4W3B8wrSX9a+nZ7GAWVwKjcY24rpRG + 2lgjUy5WLdK4Ph/XUo08i+mTheZp4KCdradI+qMhQiRV3qYw070HhD3BSun1cjkPy/bivhcg0glo + NBUWZKGJheFa9m43GGRvFNxDqWopdUrxqkFKl/6YtEWsCYrsInADnPXH5sdO6oT3HP31HOE0A9eO + eVbZYV7s2B/GFTlEjCSpatGQ4yqdKDhkRobMwZ2S7GjsEX+sED6aFJG5hAZKhO5ZTGLELHDdv/UF + H5xfesEM+k9TySEFbxsmhAeXsrZFLDlBAiZhqjOaRsgzKuBribhC2D5eEY0LZKADj4ThsRkm0AL7 + GV5m1ESuIOkECxSEJKe3/ZVwjpiAC08tieZpAsH/NVwN/wHQiK4AUwYAAA== headers: content-encoding: [gzip] - content-length: ['495'] + content-length: ['499'] content-type: [application/json] status: {code: 200, message: OK} - request: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2RCigJCqQDdYQEcThv3Jxr/cards?is_valid=True&limit=10&offset=0 + uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd/cards?is_valid=True&limit=10&offset=0 response: body: string: !!binary | - H4sIAAAAAAAAA61VWU/jMBB+51dYeeAJ2sQpvSS0KgWxAlHulZbVKnJ9tBapXWyn2wr1v6/tJE3C - od0H/BApc3hmvvlm/LoHQICRIjoYgl/2B4BX/7XilItnJy4FzjLTRi6ostJg/Ajvxnx2MX65PSU/ - b8/ww3wVX6xVUPhvD8qLGBczqpaKC+Mc+7jHwg6M4+4RYv0+jmncxSyEvU6XYtTtMIJZ3CUEQxqx - eGDNKR6wmDESHiHY7QW7i/FqlSiqs9TdK7I03WlEtpjmaa5rJ7Kn8kYrnSylNihNFsjguctNyEpP - 10uukOFSJAspjNNH4S7CghrUBEfRmbX10EjBMu1+SnOHHTebxMg/3qKuQITYInQCneJErgF8DyEn - /t4xvD+/OZ3cnDyG8vpyiqgemJdxrSaMDJ1JtXHW0swtBLuEzWZJnRgrSrhpgljW38BwisRzItDC - e13cXF3fnY8mYPx9dH8GTkaTywMwaY1aTTy1UZSaCs8N1ZXBVCHh6/jBNarE2ZLYrEmCPDtgGHUO - w95hGD1E0TCGQxi3+v0+7AyeaqFyyKx9nZwWYCup15CzmDpkPxBHLl4EY3CFuAD3PvV6Yyw1jC/+ - w34W1MGSeJtBCKOw7o1lJozaJIWBi/9uMjASCaFT244hMCqjFX9z2Bv9qPFxQ5GbQRjCipB2GN6U - GXCdrKjijFMHeyOAI/9udIK8dlAwEdg2gbw84LIHRAIhDfAsqTXcZV+waQgYSnWV/1xR5lBp+93S - /g/iKvoPEsB+pxP3nvLZ2Foofzu0gsYcBinSnkXtclPZ2J/sqTy1bw4jlHJy/GDx30/5gpvjKNyX - jGlqjsuOBoKum0smqEosluJXhfI52CLKZRPkuVhJ0exgqeiKy8wt54ohgZF2lTm/nEUB4+qrwbCA - +7X+5m3IX5AW4XqZGTvxVeNfff9bnGzbO22RXq6pvyi1phV+pXZbDFYRyLkmc5n6V6vkWC1UTd8I - 5kftE59C5yrc2/4F27WBRxcHAAA= + H4sIAAAAAAAAA61VW0/bMBR+51dYfuAJ2lza9CKhqbBpaEBBg8HENEVubFOL1M5spzRC/e+znaRJ + uGh7wA+Rci4+53znO8fPewDABEms4BT8Mj8APLuvEaeMP1pxLbCWudJiRaSRwpMf4dlPXKzn159J + cX4VZrM7OVn+wbDy3x7UF1HGH4jMJOPaOo6TEfUGQRhGQ0TH4yQkYZRQLxgNIpKgaEBxQsMI4yQg + Pg0nxpwkExpSir0hCqIR3F2crNexJCpP7b08T9OdhuerRZnmpnV8cxpvtFZxJpRGabxCOlna3Lho + 9GSTMYk0EzxeCa6t3vd2EVZEoy44kjwYWweN4DRX9qc2t9gxXcRaPDmLtgJhbIpQcWAVx2IDgtcQ + MuzuPQkvTo/VXbQ+Reehys9v8c3t4nLSQgRp8iBkYa2FXhoIdgnrIiNWnEiCme6CWNffwXCB+GPM + 0cp5fbu6uPz+dTYHJ6ez6y/geDY/OwDz3qzXxVNpSYhu8CyIagwWEnFXxy1TqBHnGTZZ4xg5dgSe + Pzj0Roeef+MPp4NwOhj2xkM/CP37VqgSMmPfJqcB2EjaNZQsJhbZN8S+jWduBheIcXDtUm83xlBD + u+Lf7GdFnURgZzPxAt9reyci51oWcWVg47+ajATxGJOFaccUaJmThr8l7J1+tPhYEGRnMPCChpBm + GF6UCZmK10QyyoiFvRPAkn83OrCsHVRMBKZNoCwP2OwBFoALDRxLWg232VdsmgKKUtXkv5SEWlT6 + brf0/4O4kvyDBIMonPjhfTkbWwPlb4sW7MwhTJFyLOrXm8rEfmdPlal9shihlOGjG4P/fspWTB/5 + 3r6gVBF9VHcUcrLpLhnYlFgtxY8K5XIwRdTLBpa5GEnVbJhJsmYit8u5YQjUwqwy61eyCFImPxoM + A7hb6y/ehvIF6WGmslybiW8a/+z632N4299pq/RKTftFaTWt8qu122qwqkDWNV6K1L1aNcdaoVr6 + TjA3au/4VDpb4d72LxntgTEXBwAA headers: content-encoding: [gzip] - content-length: ['761'] + content-length: ['762'] content-type: [application/json] status: {code: 200, message: OK} - request: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2RCigJCqQDdYQEcThv3Jxr/cards?is_valid=True&limit=10&offset=0 + uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd/cards?is_valid=True&limit=10&offset=0 response: body: string: !!binary | - H4sIAAAAAAAAA61VWU/jMBB+51dYeeAJ2sQpvSS0KgWxAlHulZbVKnJ9tBapXWyn2wr1v6/tJE3C - od0H/BApc3hmvvlm/LoHQICRIjoYgl/2B4BX/7XilItnJy4FzjLTRi6ostJg/Ajvxnx2MX65PSU/ - b8/ww3wVX6xVUPhvD8qLGBczqpaKC+Mc+7jHwg6M4+4RYv0+jmncxSyEvU6XYtTtMIJZ3CUEQxqx - eGDNKR6wmDESHiHY7QW7i/FqlSiqs9TdK7I03WlEtpjmaa5rJ7Kn8kYrnSylNihNFsjguctNyEpP - 10uukOFSJAspjNNH4S7CghrUBEfRmbX10EjBMu1+SnOHHTebxMg/3qKuQITYInQCneJErgF8DyEn - /t4xvD+/OZ3cnDyG8vpyiqgemJdxrSaMDJ1JtXHW0swtBLuEzWZJnRgrSrhpgljW38BwisRzItDC - e13cXF3fnY8mYPx9dH8GTkaTywMwaY1aTTy1UZSaCs8N1ZXBVCHh6/jBNarE2ZLYrEmCPDtgGHUO - w95hGD1E0TCGQxi3+v0+7AyeaqFyyKx9nZwWYCup15CzmDpkPxBHLl4EY3CFuAD3PvV6Yyw1jC/+ - w34W1MGSeJtBCKOw7o1lJozaJIWBi/9uMjASCaFT244hMCqjFX9z2Bv9qPFxQ5GbQRjCipB2GN6U - GXCdrKjijFMHeyOAI/9udIK8dlAwEdg2gbw84LIHRAIhDfAsqTXcZV+waQgYSnWV/1xR5lBp+93S - /g/iKvoPEsB+pxP3nvLZ2Foofzu0gsYcBinSnkXtclPZ2J/sqTy1bw4jlHJy/GDx30/5gpvjKNyX - jGlqjsuOBoKum0smqEosluJXhfI52CLKZRPkuVhJ0exgqeiKy8wt54ohgZF2lTm/nEUB4+qrwbCA - +7X+5m3IX5AW4XqZGTvxVeNfff9bnGzbO22RXq6pvyi1phV+pXZbDFYRyLkmc5n6V6vkWC1UTd8I - 5kftE59C5yrc2/4F27WBRxcHAAA= + H4sIAAAAAAAAA61VW0/bMBR+51dYfuAJ2lza9CKhqbBpaEBBg8HENEVubFOL1M5spzRC/e+znaRJ + uGh7wA+Rci4+53znO8fPewDABEms4BT8Mj8APLuvEaeMP1pxLbCWudJiRaSRwpMf4dlPXKzn159J + cX4VZrM7OVn+wbDy3x7UF1HGH4jMJOPaOo6TEfUGQRhGQ0TH4yQkYZRQLxgNIpKgaEBxQsMI4yQg + Pg0nxpwkExpSir0hCqIR3F2crNexJCpP7b08T9OdhuerRZnmpnV8cxpvtFZxJpRGabxCOlna3Lho + 9GSTMYk0EzxeCa6t3vd2EVZEoy44kjwYWweN4DRX9qc2t9gxXcRaPDmLtgJhbIpQcWAVx2IDgtcQ + MuzuPQkvTo/VXbQ+Reehys9v8c3t4nLSQgRp8iBkYa2FXhoIdgnrIiNWnEiCme6CWNffwXCB+GPM + 0cp5fbu6uPz+dTYHJ6ez6y/geDY/OwDz3qzXxVNpSYhu8CyIagwWEnFXxy1TqBHnGTZZ4xg5dgSe + Pzj0Roeef+MPp4NwOhj2xkM/CP37VqgSMmPfJqcB2EjaNZQsJhbZN8S+jWduBheIcXDtUm83xlBD + u+Lf7GdFnURgZzPxAt9reyci51oWcWVg47+ajATxGJOFaccUaJmThr8l7J1+tPhYEGRnMPCChpBm + GF6UCZmK10QyyoiFvRPAkn83OrCsHVRMBKZNoCwP2OwBFoALDRxLWg232VdsmgKKUtXkv5SEWlT6 + brf0/4O4kvyDBIMonPjhfTkbWwPlb4sW7MwhTJFyLOrXm8rEfmdPlal9shihlOGjG4P/fspWTB/5 + 3r6gVBF9VHcUcrLpLhnYlFgtxY8K5XIwRdTLBpa5GEnVbJhJsmYit8u5YQjUwqwy61eyCFImPxoM + A7hb6y/ehvIF6WGmslybiW8a/+z632N4299pq/RKTftFaTWt8qu122qwqkDWNV6K1L1aNcdaoVr6 + TjA3au/4VDpb4d72LxntgTEXBwAA headers: content-encoding: [gzip] - content-length: ['761'] + content-length: ['762'] content-type: [application/json] status: {code: 200, message: OK} - request: @@ -260,14 +260,14 @@ interactions: H4sIAAAAAAAAA5VU32/TMBB+319R5YEXWJufg1aagK28sKFVrBMChCInvqihjh059lqo+r9zcZyl EdYEL9Xd+bu77+6+5nA2mXgVKOItJge00WOkUeh5s4rILaiakRyat6ysSnUZJi9EUTSgLn3vVQfn sG/hXDNmIxsJxX8UMJURHyY2v+uAEd8GagmPpdDNuI0SijAMBRZVlPLfiCPvY5vjnQ6Ihb6bgbot - 4GvJU2hyKXZtDz8MozfB664VPlJREQRo2TLwYE+qmsE0F5VdC0I4qaB9XOmMlflkCY/DGyv5th2n + 4GvJU2hyKXZtDz9M3oT+664VPlJREQRo2TLwYE+qmsE0F5VdC0I4qaB9XOmMlflkCY/DGyv5th2n b4ZwseMg01w3SlQg28Trh2D3UHy9XWa327vVDQ0+w+/N1T7zDE87hDmZc+Oz9Yf79fmnVbBb/9ov 33+8uSZfVj+v7uM7VsZ04JJLIApoSszVQz+Iz/3kPIzXfrQILxbhfDqfR3EUfxtSGl3XQqoUcAks JZRKaNpxPPvwzrkQXdPnGkX+NAoiPwkcjeqN4JByXWXdal4GF4mfJEkQRvFAq5aC6lyVgiOXgrAG nu7VS9wc3uxMc5SxYjh4AdCSt2prL0/bWZ5fnylzxN8fRkrjg46UNaWQlcqsZ2ZNq9hT/U1R4yAb JG2Ag+fC0rKptTKssWbvuJC9njro4DmxEmhPFEVhbCeOSGrrGcuFEZLiNIafNV2oHPPTjWAn5azr QuNnRXML7W0X7u9/0uxp8NlhtPQx9OgcRUnCG2Jk1Q00CrgIZIRvU5LnQnN793HElZMTxjKSm6+C - Nxs8FxaV0he2ZvtJOzv+AW6kmdzNBQAA + Nxs8FxaV0he2ZvtJOzv+AfVvz5DNBQAA headers: content-encoding: [gzip] content-length: ['594'] @@ -277,152 +277,152 @@ interactions: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2RCigJCqQDdYQEcThv3Jxr + uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd response: body: string: !!binary | - H4sIAAAAAAAAA41Uy27bMBC85ysEnevIkp028NXtJbcU6aEtCmFNshURiXT4CGIY+vcubUmmSaXs - RYfdmSF3dsTjTZblxGojO6Z0vsl+YiHLjqcvtgR0DKvCtu2HsdZy8eygIwhhWlpFQiDWKdOGCzBc - ikFlEOknNbunYBitwSAir5blerH8tFiWT2W5WVWbqrpd35X398sf+UQhiiUoq2p993HlUajc1Z0U - pgmH4dQdu/1Wfd3yPw/bl8fP9PvjF/LUvK4e3tTlzH0jRThg3ij229GLycEirYRGkwaEqbUBY52R - uZCLDgxpLsd1zEBgMXtx0DKPHHSzHRiocDSgVDEdbIpwcwiAuCdcKavmy2VcdhcPvUCRvcRGWxNJ - Z5pEWmHUwe9Gg+ysxntoXc+lTmtRt6DNOhyTdcDbqMivAtfjWb9cftykXngv0b+dIpwX6Nopz7o4 - Tosd+v2wIo9IQNG6kS09rdKLgkfmtC883DnJnsYOxHMN5GRSQuYaGilRtuMpiQEzw/X/1nd88H7p - GTPYm2FKYAr+b5gYHl3K2Zaw5AyJmJTrvTUsQZ5QEd8oEBqIe7wSGlfISAcfCStSM4ygGfYrvszQ - Jq6g2AiLFKSi57f9H+EcMBEXn1qazNMIwv+rv+n/AoxtLhJTBgAA + H4sIAAAAAAAAA41Uy27bMBC85ysEnWvLipQ29i1oby2CAG3RokUh0CQLEZFIhY+ggqF/79KWZJpU + wlx02J0ZcmdHPFwlSYqN0qKlUqW75DcUkuRw/EKLo5ZClZumeTfVGsYfLXQCAUwJI7EPhDqhSjOO + NBN8VBlFhlnNdARpSiqkAZFeb/Jytfmw2uTf8ptdWezKcp0Xxfvb7a90pmBJX6UU622+uSlKh0LE + vmoF17U/DCP22I/fi88/Sf98//UT7b88FN3dD7mtn8j5zK4W3B8wrSX9a+nZ7GAWVwKjcY24rpRG + 2lgjUy5WLdK4Ph/XUo08i+mTheZp4KCdradI+qMhQiRV3qYw070HhD3BSun1cjkPy/bivhcg0glo + NBUWZKGJheFa9m43GGRvFNxDqWopdUrxqkFKl/6YtEWsCYrsInADnPXH5sdO6oT3HP31HOE0A9eO + eVbZYV7s2B/GFTlEjCSpatGQ4yqdKDhkRobMwZ2S7GjsEX+sED6aFJG5hAZKhO5ZTGLELHDdv/UF + H5xfesEM+k9TySEFbxsmhAeXsrZFLDlBAiZhqjOaRsgzKuBribhC2D5eEY0LZKADj4ThsRkm0AL7 + GV5m1ESuIOkECxSEJKe3/ZVwjpiAC08tieZpAsH/NVwN/wHQiK4AUwYAAA== headers: content-encoding: [gzip] - content-length: ['495'] + content-length: ['499'] content-type: [application/json] status: {code: 200, message: OK} - request: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2RCigJCqQDdYQEcThv3Jxr/cards?is_valid=True&limit=10&offset=0 + uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd/cards?is_valid=True&limit=10&offset=0 response: body: string: !!binary | - H4sIAAAAAAAAA61VWU/jMBB+51dYeeAJ2sQpvSS0KgWxAlHulZbVKnJ9tBapXWyn2wr1v6/tJE3C - od0H/BApc3hmvvlm/LoHQICRIjoYgl/2B4BX/7XilItnJy4FzjLTRi6ostJg/Ajvxnx2MX65PSU/ - b8/ww3wVX6xVUPhvD8qLGBczqpaKC+Mc+7jHwg6M4+4RYv0+jmncxSyEvU6XYtTtMIJZ3CUEQxqx - eGDNKR6wmDESHiHY7QW7i/FqlSiqs9TdK7I03WlEtpjmaa5rJ7Kn8kYrnSylNihNFsjguctNyEpP - 10uukOFSJAspjNNH4S7CghrUBEfRmbX10EjBMu1+SnOHHTebxMg/3qKuQITYInQCneJErgF8DyEn - /t4xvD+/OZ3cnDyG8vpyiqgemJdxrSaMDJ1JtXHW0swtBLuEzWZJnRgrSrhpgljW38BwisRzItDC - e13cXF3fnY8mYPx9dH8GTkaTywMwaY1aTTy1UZSaCs8N1ZXBVCHh6/jBNarE2ZLYrEmCPDtgGHUO - w95hGD1E0TCGQxi3+v0+7AyeaqFyyKx9nZwWYCup15CzmDpkPxBHLl4EY3CFuAD3PvV6Yyw1jC/+ - w34W1MGSeJtBCKOw7o1lJozaJIWBi/9uMjASCaFT244hMCqjFX9z2Bv9qPFxQ5GbQRjCipB2GN6U - GXCdrKjijFMHeyOAI/9udIK8dlAwEdg2gbw84LIHRAIhDfAsqTXcZV+waQgYSnWV/1xR5lBp+93S - /g/iKvoPEsB+pxP3nvLZ2Foofzu0gsYcBinSnkXtclPZ2J/sqTy1bw4jlHJy/GDx30/5gpvjKNyX - jGlqjsuOBoKum0smqEosluJXhfI52CLKZRPkuVhJ0exgqeiKy8wt54ohgZF2lTm/nEUB4+qrwbCA - +7X+5m3IX5AW4XqZGTvxVeNfff9bnGzbO22RXq6pvyi1phV+pXZbDFYRyLkmc5n6V6vkWC1UTd8I - 5kftE59C5yrc2/4F27WBRxcHAAA= + H4sIAAAAAAAAA61VW0/bMBR+51dYfuAJ2lza9CKhqbBpaEBBg8HENEVubFOL1M5spzRC/e+znaRJ + uGh7wA+Rci4+53znO8fPewDABEms4BT8Mj8APLuvEaeMP1pxLbCWudJiRaSRwpMf4dlPXKzn159J + cX4VZrM7OVn+wbDy3x7UF1HGH4jMJOPaOo6TEfUGQRhGQ0TH4yQkYZRQLxgNIpKgaEBxQsMI4yQg + Pg0nxpwkExpSir0hCqIR3F2crNexJCpP7b08T9OdhuerRZnmpnV8cxpvtFZxJpRGabxCOlna3Lho + 9GSTMYk0EzxeCa6t3vd2EVZEoy44kjwYWweN4DRX9qc2t9gxXcRaPDmLtgJhbIpQcWAVx2IDgtcQ + MuzuPQkvTo/VXbQ+Reehys9v8c3t4nLSQgRp8iBkYa2FXhoIdgnrIiNWnEiCme6CWNffwXCB+GPM + 0cp5fbu6uPz+dTYHJ6ez6y/geDY/OwDz3qzXxVNpSYhu8CyIagwWEnFXxy1TqBHnGTZZ4xg5dgSe + Pzj0Roeef+MPp4NwOhj2xkM/CP37VqgSMmPfJqcB2EjaNZQsJhbZN8S+jWduBheIcXDtUm83xlBD + u+Lf7GdFnURgZzPxAt9reyci51oWcWVg47+ajATxGJOFaccUaJmThr8l7J1+tPhYEGRnMPCChpBm + GF6UCZmK10QyyoiFvRPAkn83OrCsHVRMBKZNoCwP2OwBFoALDRxLWg232VdsmgKKUtXkv5SEWlT6 + brf0/4O4kvyDBIMonPjhfTkbWwPlb4sW7MwhTJFyLOrXm8rEfmdPlal9shihlOGjG4P/fspWTB/5 + 3r6gVBF9VHcUcrLpLhnYlFgtxY8K5XIwRdTLBpa5GEnVbJhJsmYit8u5YQjUwqwy61eyCFImPxoM + A7hb6y/ehvIF6WGmslybiW8a/+z632N4299pq/RKTftFaTWt8qu122qwqkDWNV6K1L1aNcdaoVr6 + TjA3au/4VDpb4d72LxntgTEXBwAA headers: content-encoding: [gzip] - content-length: ['761'] + content-length: ['762'] content-type: [application/json] status: {code: 200, message: OK} - request: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2RCigJCqQDdYQEcThv3Jxr/cards?is_valid=True&limit=10&offset=0 + uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd/cards?is_valid=True&limit=10&offset=0 response: body: string: !!binary | - H4sIAAAAAAAAA61VWU/jMBB+51dYeeAJ2sQpvSS0KgWxAlHulZbVKnJ9tBapXWyn2wr1v6/tJE3C - od0H/BApc3hmvvlm/LoHQICRIjoYgl/2B4BX/7XilItnJy4FzjLTRi6ostJg/Ajvxnx2MX65PSU/ - b8/ww3wVX6xVUPhvD8qLGBczqpaKC+Mc+7jHwg6M4+4RYv0+jmncxSyEvU6XYtTtMIJZ3CUEQxqx - eGDNKR6wmDESHiHY7QW7i/FqlSiqs9TdK7I03WlEtpjmaa5rJ7Kn8kYrnSylNihNFsjguctNyEpP - 10uukOFSJAspjNNH4S7CghrUBEfRmbX10EjBMu1+SnOHHTebxMg/3qKuQITYInQCneJErgF8DyEn - /t4xvD+/OZ3cnDyG8vpyiqgemJdxrSaMDJ1JtXHW0swtBLuEzWZJnRgrSrhpgljW38BwisRzItDC - e13cXF3fnY8mYPx9dH8GTkaTywMwaY1aTTy1UZSaCs8N1ZXBVCHh6/jBNarE2ZLYrEmCPDtgGHUO - w95hGD1E0TCGQxi3+v0+7AyeaqFyyKx9nZwWYCup15CzmDpkPxBHLl4EY3CFuAD3PvV6Yyw1jC/+ - w34W1MGSeJtBCKOw7o1lJozaJIWBi/9uMjASCaFT244hMCqjFX9z2Bv9qPFxQ5GbQRjCipB2GN6U - GXCdrKjijFMHeyOAI/9udIK8dlAwEdg2gbw84LIHRAIhDfAsqTXcZV+waQgYSnWV/1xR5lBp+93S - /g/iKvoPEsB+pxP3nvLZ2Foofzu0gsYcBinSnkXtclPZ2J/sqTy1bw4jlHJy/GDx30/5gpvjKNyX - jGlqjsuOBoKum0smqEosluJXhfI52CLKZRPkuVhJ0exgqeiKy8wt54ohgZF2lTm/nEUB4+qrwbCA - +7X+5m3IX5AW4XqZGTvxVeNfff9bnGzbO22RXq6pvyi1phV+pXZbDFYRyLkmc5n6V6vkWC1UTd8I - 5kftE59C5yrc2/4F27WBRxcHAAA= + H4sIAAAAAAAAA61VW0/bMBR+51dYfuAJ2lza9CKhqbBpaEBBg8HENEVubFOL1M5spzRC/e+znaRJ + uGh7wA+Rci4+53znO8fPewDABEms4BT8Mj8APLuvEaeMP1pxLbCWudJiRaSRwpMf4dlPXKzn159J + cX4VZrM7OVn+wbDy3x7UF1HGH4jMJOPaOo6TEfUGQRhGQ0TH4yQkYZRQLxgNIpKgaEBxQsMI4yQg + Pg0nxpwkExpSir0hCqIR3F2crNexJCpP7b08T9OdhuerRZnmpnV8cxpvtFZxJpRGabxCOlna3Lho + 9GSTMYk0EzxeCa6t3vd2EVZEoy44kjwYWweN4DRX9qc2t9gxXcRaPDmLtgJhbIpQcWAVx2IDgtcQ + MuzuPQkvTo/VXbQ+Reehys9v8c3t4nLSQgRp8iBkYa2FXhoIdgnrIiNWnEiCme6CWNffwXCB+GPM + 0cp5fbu6uPz+dTYHJ6ez6y/geDY/OwDz3qzXxVNpSYhu8CyIagwWEnFXxy1TqBHnGTZZ4xg5dgSe + Pzj0Roeef+MPp4NwOhj2xkM/CP37VqgSMmPfJqcB2EjaNZQsJhbZN8S+jWduBheIcXDtUm83xlBD + u+Lf7GdFnURgZzPxAt9reyci51oWcWVg47+ajATxGJOFaccUaJmThr8l7J1+tPhYEGRnMPCChpBm + GF6UCZmK10QyyoiFvRPAkn83OrCsHVRMBKZNoCwP2OwBFoALDRxLWg232VdsmgKKUtXkv5SEWlT6 + brf0/4O4kvyDBIMonPjhfTkbWwPlb4sW7MwhTJFyLOrXm8rEfmdPlal9shihlOGjG4P/fspWTB/5 + 3r6gVBF9VHcUcrLpLhnYlFgtxY8K5XIwRdTLBpa5GEnVbJhJsmYit8u5YQjUwqwy61eyCFImPxoM + A7hb6y/ehvIF6WGmslybiW8a/+z632N4299pq/RKTftFaTWt8qu122qwqkDWNV6K1L1aNcdaoVr6 + TjA3au/4VDpb4d72LxntgTEXBwAA headers: content-encoding: [gzip] - content-length: ['761'] + content-length: ['762'] content-type: [application/json] status: {code: 200, message: OK} - request: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2RCigJCqQDdYQEcThv3Jxr + uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd response: body: string: !!binary | - H4sIAAAAAAAAA41Uy27bMBC85ysEnevIkp028NXtJbcU6aEtCmFNshURiXT4CGIY+vcubUmmSaXs - RYfdmSF3dsTjTZblxGojO6Z0vsl+YiHLjqcvtgR0DKvCtu2HsdZy8eygIwhhWlpFQiDWKdOGCzBc - ikFlEOknNbunYBitwSAir5blerH8tFiWT2W5WVWbqrpd35X398sf+UQhiiUoq2p993HlUajc1Z0U - pgmH4dQdu/1Wfd3yPw/bl8fP9PvjF/LUvK4e3tTlzH0jRThg3ij229GLycEirYRGkwaEqbUBY52R - uZCLDgxpLsd1zEBgMXtx0DKPHHSzHRiocDSgVDEdbIpwcwiAuCdcKavmy2VcdhcPvUCRvcRGWxNJ - Z5pEWmHUwe9Gg+ysxntoXc+lTmtRt6DNOhyTdcDbqMivAtfjWb9cftykXngv0b+dIpwX6Nopz7o4 - Tosd+v2wIo9IQNG6kS09rdKLgkfmtC883DnJnsYOxHMN5GRSQuYaGilRtuMpiQEzw/X/1nd88H7p - GTPYm2FKYAr+b5gYHl3K2Zaw5AyJmJTrvTUsQZ5QEd8oEBqIe7wSGlfISAcfCStSM4ygGfYrvszQ - Jq6g2AiLFKSi57f9H+EcMBEXn1qazNMIwv+rv+n/AoxtLhJTBgAA + H4sIAAAAAAAAA41Uy27bMBC85ysEnWvLipQ29i1oby2CAG3RokUh0CQLEZFIhY+ggqF/79KWZJpU + wlx02J0ZcmdHPFwlSYqN0qKlUqW75DcUkuRw/EKLo5ZClZumeTfVGsYfLXQCAUwJI7EPhDqhSjOO + NBN8VBlFhlnNdARpSiqkAZFeb/Jytfmw2uTf8ptdWezKcp0Xxfvb7a90pmBJX6UU622+uSlKh0LE + vmoF17U/DCP22I/fi88/Sf98//UT7b88FN3dD7mtn8j5zK4W3B8wrSX9a+nZ7GAWVwKjcY24rpRG + 2lgjUy5WLdK4Ph/XUo08i+mTheZp4KCdradI+qMhQiRV3qYw070HhD3BSun1cjkPy/bivhcg0glo + NBUWZKGJheFa9m43GGRvFNxDqWopdUrxqkFKl/6YtEWsCYrsInADnPXH5sdO6oT3HP31HOE0A9eO + eVbZYV7s2B/GFTlEjCSpatGQ4yqdKDhkRobMwZ2S7GjsEX+sED6aFJG5hAZKhO5ZTGLELHDdv/UF + H5xfesEM+k9TySEFbxsmhAeXsrZFLDlBAiZhqjOaRsgzKuBribhC2D5eEY0LZKADj4ThsRkm0AL7 + GV5m1ESuIOkECxSEJKe3/ZVwjpiAC08tieZpAsH/NVwN/wHQiK4AUwYAAA== headers: content-encoding: [gzip] - content-length: ['495'] + content-length: ['499'] content-type: [application/json] status: {code: 200, message: OK} - request: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2RCigJCqQDdYQEcThv3Jxr/cards?is_valid=True&limit=10&offset=0 + uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd/cards?is_valid=True&limit=10&offset=0 response: body: string: !!binary | - H4sIAAAAAAAAA61VWU/jMBB+51dYeeAJ2sQpvSS0KgWxAlHulZbVKnJ9tBapXWyn2wr1v6/tJE3C - od0H/BApc3hmvvlm/LoHQICRIjoYgl/2B4BX/7XilItnJy4FzjLTRi6ostJg/Ajvxnx2MX65PSU/ - b8/ww3wVX6xVUPhvD8qLGBczqpaKC+Mc+7jHwg6M4+4RYv0+jmncxSyEvU6XYtTtMIJZ3CUEQxqx - eGDNKR6wmDESHiHY7QW7i/FqlSiqs9TdK7I03WlEtpjmaa5rJ7Kn8kYrnSylNihNFsjguctNyEpP - 10uukOFSJAspjNNH4S7CghrUBEfRmbX10EjBMu1+SnOHHTebxMg/3qKuQITYInQCneJErgF8DyEn - /t4xvD+/OZ3cnDyG8vpyiqgemJdxrSaMDJ1JtXHW0swtBLuEzWZJnRgrSrhpgljW38BwisRzItDC - e13cXF3fnY8mYPx9dH8GTkaTywMwaY1aTTy1UZSaCs8N1ZXBVCHh6/jBNarE2ZLYrEmCPDtgGHUO - w95hGD1E0TCGQxi3+v0+7AyeaqFyyKx9nZwWYCup15CzmDpkPxBHLl4EY3CFuAD3PvV6Yyw1jC/+ - w34W1MGSeJtBCKOw7o1lJozaJIWBi/9uMjASCaFT244hMCqjFX9z2Bv9qPFxQ5GbQRjCipB2GN6U - GXCdrKjijFMHeyOAI/9udIK8dlAwEdg2gbw84LIHRAIhDfAsqTXcZV+waQgYSnWV/1xR5lBp+93S - /g/iKvoPEsB+pxP3nvLZ2Foofzu0gsYcBinSnkXtclPZ2J/sqTy1bw4jlHJy/GDx30/5gpvjKNyX - jGlqjsuOBoKum0smqEosluJXhfI52CLKZRPkuVhJ0exgqeiKy8wt54ohgZF2lTm/nEUB4+qrwbCA - +7X+5m3IX5AW4XqZGTvxVeNfff9bnGzbO22RXq6pvyi1phV+pXZbDFYRyLkmc5n6V6vkWC1UTd8I - 5kftE59C5yrc2/4F27WBRxcHAAA= + H4sIAAAAAAAAA61VW0/bMBR+51dYfuAJ2lza9CKhqbBpaEBBg8HENEVubFOL1M5spzRC/e+znaRJ + uGh7wA+Rci4+53znO8fPewDABEms4BT8Mj8APLuvEaeMP1pxLbCWudJiRaSRwpMf4dlPXKzn159J + cX4VZrM7OVn+wbDy3x7UF1HGH4jMJOPaOo6TEfUGQRhGQ0TH4yQkYZRQLxgNIpKgaEBxQsMI4yQg + Pg0nxpwkExpSir0hCqIR3F2crNexJCpP7b08T9OdhuerRZnmpnV8cxpvtFZxJpRGabxCOlna3Lho + 9GSTMYk0EzxeCa6t3vd2EVZEoy44kjwYWweN4DRX9qc2t9gxXcRaPDmLtgJhbIpQcWAVx2IDgtcQ + MuzuPQkvTo/VXbQ+Reehys9v8c3t4nLSQgRp8iBkYa2FXhoIdgnrIiNWnEiCme6CWNffwXCB+GPM + 0cp5fbu6uPz+dTYHJ6ez6y/geDY/OwDz3qzXxVNpSYhu8CyIagwWEnFXxy1TqBHnGTZZ4xg5dgSe + Pzj0Roeef+MPp4NwOhj2xkM/CP37VqgSMmPfJqcB2EjaNZQsJhbZN8S+jWduBheIcXDtUm83xlBD + u+Lf7GdFnURgZzPxAt9reyci51oWcWVg47+ajATxGJOFaccUaJmThr8l7J1+tPhYEGRnMPCChpBm + GF6UCZmK10QyyoiFvRPAkn83OrCsHVRMBKZNoCwP2OwBFoALDRxLWg232VdsmgKKUtXkv5SEWlT6 + brf0/4O4kvyDBIMonPjhfTkbWwPlb4sW7MwhTJFyLOrXm8rEfmdPlal9shihlOGjG4P/fspWTB/5 + 3r6gVBF9VHcUcrLpLhnYlFgtxY8K5XIwRdTLBpa5GEnVbJhJsmYit8u5YQjUwqwy61eyCFImPxoM + A7hb6y/ehvIF6WGmslybiW8a/+z632N4299pq/RKTftFaTWt8qu122qwqkDWNV6K1L1aNcdaoVr6 + TjA3au/4VDpb4d72LxntgTEXBwAA headers: content-encoding: [gzip] - content-length: ['761'] + content-length: ['762'] content-type: [application/json] status: {code: 200, message: OK} - request: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2RCigJCqQDdYQEcThv3Jxr/cards?is_valid=True&limit=10&offset=0 + uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd/cards?is_valid=True&limit=10&offset=0 response: body: string: !!binary | - H4sIAAAAAAAAA61VWU/jMBB+51dYeeAJ2sQpvSS0KgWxAlHulZbVKnJ9tBapXWyn2wr1v6/tJE3C - od0H/BApc3hmvvlm/LoHQICRIjoYgl/2B4BX/7XilItnJy4FzjLTRi6ostJg/Ajvxnx2MX65PSU/ - b8/ww3wVX6xVUPhvD8qLGBczqpaKC+Mc+7jHwg6M4+4RYv0+jmncxSyEvU6XYtTtMIJZ3CUEQxqx - eGDNKR6wmDESHiHY7QW7i/FqlSiqs9TdK7I03WlEtpjmaa5rJ7Kn8kYrnSylNihNFsjguctNyEpP - 10uukOFSJAspjNNH4S7CghrUBEfRmbX10EjBMu1+SnOHHTebxMg/3qKuQITYInQCneJErgF8DyEn - /t4xvD+/OZ3cnDyG8vpyiqgemJdxrSaMDJ1JtXHW0swtBLuEzWZJnRgrSrhpgljW38BwisRzItDC - e13cXF3fnY8mYPx9dH8GTkaTywMwaY1aTTy1UZSaCs8N1ZXBVCHh6/jBNarE2ZLYrEmCPDtgGHUO - w95hGD1E0TCGQxi3+v0+7AyeaqFyyKx9nZwWYCup15CzmDpkPxBHLl4EY3CFuAD3PvV6Yyw1jC/+ - w34W1MGSeJtBCKOw7o1lJozaJIWBi/9uMjASCaFT244hMCqjFX9z2Bv9qPFxQ5GbQRjCipB2GN6U - GXCdrKjijFMHeyOAI/9udIK8dlAwEdg2gbw84LIHRAIhDfAsqTXcZV+waQgYSnWV/1xR5lBp+93S - /g/iKvoPEsB+pxP3nvLZ2Foofzu0gsYcBinSnkXtclPZ2J/sqTy1bw4jlHJy/GDx30/5gpvjKNyX - jGlqjsuOBoKum0smqEosluJXhfI52CLKZRPkuVhJ0exgqeiKy8wt54ohgZF2lTm/nEUB4+qrwbCA - +7X+5m3IX5AW4XqZGTvxVeNfff9bnGzbO22RXq6pvyi1phV+pXZbDFYRyLkmc5n6V6vkWC1UTd8I - 5kftE59C5yrc2/4F27WBRxcHAAA= + H4sIAAAAAAAAA61VW0/bMBR+51dYfuAJ2lza9CKhqbBpaEBBg8HENEVubFOL1M5spzRC/e+znaRJ + uGh7wA+Rci4+53znO8fPewDABEms4BT8Mj8APLuvEaeMP1pxLbCWudJiRaSRwpMf4dlPXKzn159J + cX4VZrM7OVn+wbDy3x7UF1HGH4jMJOPaOo6TEfUGQRhGQ0TH4yQkYZRQLxgNIpKgaEBxQsMI4yQg + Pg0nxpwkExpSir0hCqIR3F2crNexJCpP7b08T9OdhuerRZnmpnV8cxpvtFZxJpRGabxCOlna3Lho + 9GSTMYk0EzxeCa6t3vd2EVZEoy44kjwYWweN4DRX9qc2t9gxXcRaPDmLtgJhbIpQcWAVx2IDgtcQ + MuzuPQkvTo/VXbQ+Reehys9v8c3t4nLSQgRp8iBkYa2FXhoIdgnrIiNWnEiCme6CWNffwXCB+GPM + 0cp5fbu6uPz+dTYHJ6ez6y/geDY/OwDz3qzXxVNpSYhu8CyIagwWEnFXxy1TqBHnGTZZ4xg5dgSe + Pzj0Roeef+MPp4NwOhj2xkM/CP37VqgSMmPfJqcB2EjaNZQsJhbZN8S+jWduBheIcXDtUm83xlBD + u+Lf7GdFnURgZzPxAt9reyci51oWcWVg47+ajATxGJOFaccUaJmThr8l7J1+tPhYEGRnMPCChpBm + GF6UCZmK10QyyoiFvRPAkn83OrCsHVRMBKZNoCwP2OwBFoALDRxLWg232VdsmgKKUtXkv5SEWlT6 + brf0/4O4kvyDBIMonPjhfTkbWwPlb4sW7MwhTJFyLOrXm8rEfmdPlal9shihlOGjG4P/fspWTB/5 + 3r6gVBF9VHcUcrLpLhnYlFgtxY8K5XIwRdTLBpa5GEnVbJhJsmYit8u5YQjUwqwy61eyCFImPxoM + A7hb6y/ehvIF6WGmslybiW8a/+z632N4299pq/RKTftFaTWt8qu122qwqkDWNV6K1L1aNcdaoVr6 + TjA3au/4VDpb4d72LxntgTEXBwAA headers: content-encoding: [gzip] - content-length: ['761'] + content-length: ['762'] content-type: [application/json] status: {code: 200, message: OK} - request: @@ -436,14 +436,14 @@ interactions: H4sIAAAAAAAAA5VU32/TMBB+319R5YEXWJufg1aagK28sKFVrBMChCInvqihjh059lqo+r9zcZyl EdYEL9Xd+bu77+6+5nA2mXgVKOItJge00WOkUeh5s4rILaiakRyat6ysSnUZJi9EUTSgLn3vVQfn sG/hXDNmIxsJxX8UMJURHyY2v+uAEd8GagmPpdDNuI0SijAMBRZVlPLfiCPvY5vjnQ6Ihb6bgbot - 4GvJU2hyKXZtDz8MozfB664VPlJREQRo2TLwYE+qmsE0F5VdC0I4qaB9XOmMlflkCY/DGyv5th2n + 4GvJU2hyKXZtDz9M3oT+664VPlJREQRo2TLwYE+qmsE0F5VdC0I4qaB9XOmMlflkCY/DGyv5th2n b4ZwseMg01w3SlQg28Trh2D3UHy9XWa327vVDQ0+w+/N1T7zDE87hDmZc+Oz9Yf79fmnVbBb/9ov 33+8uSZfVj+v7uM7VsZ04JJLIApoSszVQz+Iz/3kPIzXfrQILxbhfDqfR3EUfxtSGl3XQqoUcAks JZRKaNpxPPvwzrkQXdPnGkX+NAoiPwkcjeqN4JByXWXdal4GF4mfJEkQRvFAq5aC6lyVgiOXgrAG nu7VS9wc3uxMc5SxYjh4AdCSt2prL0/bWZ5fnylzxN8fRkrjg46UNaWQlcqsZ2ZNq9hT/U1R4yAb JG2Ag+fC0rKptTKssWbvuJC9njro4DmxEmhPFEVhbCeOSGrrGcuFEZLiNIafNV2oHPPTjWAn5azr QuNnRXML7W0X7u9/0uxp8NlhtPQx9OgcRUnCG2Jk1Q00CrgIZIRvU5LnQnN793HElZMTxjKSm6+C - Nxs8FxaV0he2ZvtJOzv+AW6kmdzNBQAA + Nxs8FxaV0he2ZvtJOzv+AfVvz5DNBQAA headers: content-encoding: [gzip] content-length: ['594'] @@ -453,76 +453,76 @@ interactions: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2RCigJCqQDdYQEcThv3Jxr + uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd response: body: string: !!binary | - H4sIAAAAAAAAA41Uy27bMBC85ysEnevIkp028NXtJbcU6aEtCmFNshURiXT4CGIY+vcubUmmSaXs - RYfdmSF3dsTjTZblxGojO6Z0vsl+YiHLjqcvtgR0DKvCtu2HsdZy8eygIwhhWlpFQiDWKdOGCzBc - ikFlEOknNbunYBitwSAir5blerH8tFiWT2W5WVWbqrpd35X398sf+UQhiiUoq2p993HlUajc1Z0U - pgmH4dQdu/1Wfd3yPw/bl8fP9PvjF/LUvK4e3tTlzH0jRThg3ij229GLycEirYRGkwaEqbUBY52R - uZCLDgxpLsd1zEBgMXtx0DKPHHSzHRiocDSgVDEdbIpwcwiAuCdcKavmy2VcdhcPvUCRvcRGWxNJ - Z5pEWmHUwe9Gg+ysxntoXc+lTmtRt6DNOhyTdcDbqMivAtfjWb9cftykXngv0b+dIpwX6Nopz7o4 - Tosd+v2wIo9IQNG6kS09rdKLgkfmtC883DnJnsYOxHMN5GRSQuYaGilRtuMpiQEzw/X/1nd88H7p - GTPYm2FKYAr+b5gYHl3K2Zaw5AyJmJTrvTUsQZ5QEd8oEBqIe7wSGlfISAcfCStSM4ygGfYrvszQ - Jq6g2AiLFKSi57f9H+EcMBEXn1qazNMIwv+rv+n/AoxtLhJTBgAA + H4sIAAAAAAAAA41Uy27bMBC85ysEnWvLipQ29i1oby2CAG3RokUh0CQLEZFIhY+ggqF/79KWZJpU + wlx02J0ZcmdHPFwlSYqN0qKlUqW75DcUkuRw/EKLo5ZClZumeTfVGsYfLXQCAUwJI7EPhDqhSjOO + NBN8VBlFhlnNdARpSiqkAZFeb/Jytfmw2uTf8ptdWezKcp0Xxfvb7a90pmBJX6UU622+uSlKh0LE + vmoF17U/DCP22I/fi88/Sf98//UT7b88FN3dD7mtn8j5zK4W3B8wrSX9a+nZ7GAWVwKjcY24rpRG + 2lgjUy5WLdK4Ph/XUo08i+mTheZp4KCdradI+qMhQiRV3qYw070HhD3BSun1cjkPy/bivhcg0glo + NBUWZKGJheFa9m43GGRvFNxDqWopdUrxqkFKl/6YtEWsCYrsInADnPXH5sdO6oT3HP31HOE0A9eO + eVbZYV7s2B/GFTlEjCSpatGQ4yqdKDhkRobMwZ2S7GjsEX+sED6aFJG5hAZKhO5ZTGLELHDdv/UF + H5xfesEM+k9TySEFbxsmhAeXsrZFLDlBAiZhqjOaRsgzKuBribhC2D5eEY0LZKADj4ThsRkm0AL7 + GV5m1ESuIOkECxSEJKe3/ZVwjpiAC08tieZpAsH/NVwN/wHQiK4AUwYAAA== headers: content-encoding: [gzip] - content-length: ['495'] + content-length: ['499'] content-type: [application/json] status: {code: 200, message: OK} - request: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2RCigJCqQDdYQEcThv3Jxr/cards?is_valid=True&limit=10&offset=0 + uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd/cards?is_valid=True&limit=10&offset=0 response: body: string: !!binary | - H4sIAAAAAAAAA61VWU/jMBB+51dYeeAJ2sQpvSS0KgWxAlHulZbVKnJ9tBapXWyn2wr1v6/tJE3C - od0H/BApc3hmvvlm/LoHQICRIjoYgl/2B4BX/7XilItnJy4FzjLTRi6ostJg/Ajvxnx2MX65PSU/ - b8/ww3wVX6xVUPhvD8qLGBczqpaKC+Mc+7jHwg6M4+4RYv0+jmncxSyEvU6XYtTtMIJZ3CUEQxqx - eGDNKR6wmDESHiHY7QW7i/FqlSiqs9TdK7I03WlEtpjmaa5rJ7Kn8kYrnSylNihNFsjguctNyEpP - 10uukOFSJAspjNNH4S7CghrUBEfRmbX10EjBMu1+SnOHHTebxMg/3qKuQITYInQCneJErgF8DyEn - /t4xvD+/OZ3cnDyG8vpyiqgemJdxrSaMDJ1JtXHW0swtBLuEzWZJnRgrSrhpgljW38BwisRzItDC - e13cXF3fnY8mYPx9dH8GTkaTywMwaY1aTTy1UZSaCs8N1ZXBVCHh6/jBNarE2ZLYrEmCPDtgGHUO - w95hGD1E0TCGQxi3+v0+7AyeaqFyyKx9nZwWYCup15CzmDpkPxBHLl4EY3CFuAD3PvV6Yyw1jC/+ - w34W1MGSeJtBCKOw7o1lJozaJIWBi/9uMjASCaFT244hMCqjFX9z2Bv9qPFxQ5GbQRjCipB2GN6U - GXCdrKjijFMHeyOAI/9udIK8dlAwEdg2gbw84LIHRAIhDfAsqTXcZV+waQgYSnWV/1xR5lBp+93S - /g/iKvoPEsB+pxP3nvLZ2Foofzu0gsYcBinSnkXtclPZ2J/sqTy1bw4jlHJy/GDx30/5gpvjKNyX - jGlqjsuOBoKum0smqEosluJXhfI52CLKZRPkuVhJ0exgqeiKy8wt54ohgZF2lTm/nEUB4+qrwbCA - +7X+5m3IX5AW4XqZGTvxVeNfff9bnGzbO22RXq6pvyi1phV+pXZbDFYRyLkmc5n6V6vkWC1UTd8I - 5kftE59C5yrc2/4F27WBRxcHAAA= + H4sIAAAAAAAAA61VW0/bMBR+51dYfuAJ2lza9CKhqbBpaEBBg8HENEVubFOL1M5spzRC/e+znaRJ + uGh7wA+Rci4+53znO8fPewDABEms4BT8Mj8APLuvEaeMP1pxLbCWudJiRaSRwpMf4dlPXKzn159J + cX4VZrM7OVn+wbDy3x7UF1HGH4jMJOPaOo6TEfUGQRhGQ0TH4yQkYZRQLxgNIpKgaEBxQsMI4yQg + Pg0nxpwkExpSir0hCqIR3F2crNexJCpP7b08T9OdhuerRZnmpnV8cxpvtFZxJpRGabxCOlna3Lho + 9GSTMYk0EzxeCa6t3vd2EVZEoy44kjwYWweN4DRX9qc2t9gxXcRaPDmLtgJhbIpQcWAVx2IDgtcQ + MuzuPQkvTo/VXbQ+Reehys9v8c3t4nLSQgRp8iBkYa2FXhoIdgnrIiNWnEiCme6CWNffwXCB+GPM + 0cp5fbu6uPz+dTYHJ6ez6y/geDY/OwDz3qzXxVNpSYhu8CyIagwWEnFXxy1TqBHnGTZZ4xg5dgSe + Pzj0Roeef+MPp4NwOhj2xkM/CP37VqgSMmPfJqcB2EjaNZQsJhbZN8S+jWduBheIcXDtUm83xlBD + u+Lf7GdFnURgZzPxAt9reyci51oWcWVg47+ajATxGJOFaccUaJmThr8l7J1+tPhYEGRnMPCChpBm + GF6UCZmK10QyyoiFvRPAkn83OrCsHVRMBKZNoCwP2OwBFoALDRxLWg232VdsmgKKUtXkv5SEWlT6 + brf0/4O4kvyDBIMonPjhfTkbWwPlb4sW7MwhTJFyLOrXm8rEfmdPlal9shihlOGjG4P/fspWTB/5 + 3r6gVBF9VHcUcrLpLhnYlFgtxY8K5XIwRdTLBpa5GEnVbJhJsmYit8u5YQjUwqwy61eyCFImPxoM + A7hb6y/ehvIF6WGmslybiW8a/+z632N4299pq/RKTftFaTWt8qu122qwqkDWNV6K1L1aNcdaoVr6 + TjA3au/4VDpb4d72LxntgTEXBwAA headers: content-encoding: [gzip] - content-length: ['761'] + content-length: ['762'] content-type: [application/json] status: {code: 200, message: OK} - request: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2RCigJCqQDdYQEcThv3Jxr/cards?is_valid=True&limit=10&offset=0 + uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd/cards?is_valid=True&limit=10&offset=0 response: body: string: !!binary | - H4sIAAAAAAAAA61VWU/jMBB+51dYeeAJ2sQpvSS0KgWxAlHulZbVKnJ9tBapXWyn2wr1v6/tJE3C - od0H/BApc3hmvvlm/LoHQICRIjoYgl/2B4BX/7XilItnJy4FzjLTRi6ostJg/Ajvxnx2MX65PSU/ - b8/ww3wVX6xVUPhvD8qLGBczqpaKC+Mc+7jHwg6M4+4RYv0+jmncxSyEvU6XYtTtMIJZ3CUEQxqx - eGDNKR6wmDESHiHY7QW7i/FqlSiqs9TdK7I03WlEtpjmaa5rJ7Kn8kYrnSylNihNFsjguctNyEpP - 10uukOFSJAspjNNH4S7CghrUBEfRmbX10EjBMu1+SnOHHTebxMg/3qKuQITYInQCneJErgF8DyEn - /t4xvD+/OZ3cnDyG8vpyiqgemJdxrSaMDJ1JtXHW0swtBLuEzWZJnRgrSrhpgljW38BwisRzItDC - e13cXF3fnY8mYPx9dH8GTkaTywMwaY1aTTy1UZSaCs8N1ZXBVCHh6/jBNarE2ZLYrEmCPDtgGHUO - w95hGD1E0TCGQxi3+v0+7AyeaqFyyKx9nZwWYCup15CzmDpkPxBHLl4EY3CFuAD3PvV6Yyw1jC/+ - w34W1MGSeJtBCKOw7o1lJozaJIWBi/9uMjASCaFT244hMCqjFX9z2Bv9qPFxQ5GbQRjCipB2GN6U - GXCdrKjijFMHeyOAI/9udIK8dlAwEdg2gbw84LIHRAIhDfAsqTXcZV+waQgYSnWV/1xR5lBp+93S - /g/iKvoPEsB+pxP3nvLZ2Foofzu0gsYcBinSnkXtclPZ2J/sqTy1bw4jlHJy/GDx30/5gpvjKNyX - jGlqjsuOBoKum0smqEosluJXhfI52CLKZRPkuVhJ0exgqeiKy8wt54ohgZF2lTm/nEUB4+qrwbCA - +7X+5m3IX5AW4XqZGTvxVeNfff9bnGzbO22RXq6pvyi1phV+pXZbDFYRyLkmc5n6V6vkWC1UTd8I - 5kftE59C5yrc2/4F27WBRxcHAAA= + H4sIAAAAAAAAA61VW0/bMBR+51dYfuAJ2lza9CKhqbBpaEBBg8HENEVubFOL1M5spzRC/e+znaRJ + uGh7wA+Rci4+53znO8fPewDABEms4BT8Mj8APLuvEaeMP1pxLbCWudJiRaSRwpMf4dlPXKzn159J + cX4VZrM7OVn+wbDy3x7UF1HGH4jMJOPaOo6TEfUGQRhGQ0TH4yQkYZRQLxgNIpKgaEBxQsMI4yQg + Pg0nxpwkExpSir0hCqIR3F2crNexJCpP7b08T9OdhuerRZnmpnV8cxpvtFZxJpRGabxCOlna3Lho + 9GSTMYk0EzxeCa6t3vd2EVZEoy44kjwYWweN4DRX9qc2t9gxXcRaPDmLtgJhbIpQcWAVx2IDgtcQ + MuzuPQkvTo/VXbQ+Reehys9v8c3t4nLSQgRp8iBkYa2FXhoIdgnrIiNWnEiCme6CWNffwXCB+GPM + 0cp5fbu6uPz+dTYHJ6ez6y/geDY/OwDz3qzXxVNpSYhu8CyIagwWEnFXxy1TqBHnGTZZ4xg5dgSe + Pzj0Roeef+MPp4NwOhj2xkM/CP37VqgSMmPfJqcB2EjaNZQsJhbZN8S+jWduBheIcXDtUm83xlBD + u+Lf7GdFnURgZzPxAt9reyci51oWcWVg47+ajATxGJOFaccUaJmThr8l7J1+tPhYEGRnMPCChpBm + GF6UCZmK10QyyoiFvRPAkn83OrCsHVRMBKZNoCwP2OwBFoALDRxLWg232VdsmgKKUtXkv5SEWlT6 + brf0/4O4kvyDBIMonPjhfTkbWwPlb4sW7MwhTJFyLOrXm8rEfmdPlal9shihlOGjG4P/fspWTB/5 + 3r6gVBF9VHcUcrLpLhnYlFgtxY8K5XIwRdTLBpa5GEnVbJhJsmYit8u5YQjUwqwy61eyCFImPxoM + A7hb6y/ehvIF6WGmslybiW8a/+z632N4299pq/RKTftFaTWt8qu122qwqkDWNV6K1L1aNcdaoVr6 + TjA3au/4VDpb4d72LxntgTEXBwAA headers: content-encoding: [gzip] - content-length: ['761'] + content-length: ['762'] content-type: [application/json] status: {code: 200, message: OK} - request: @@ -536,14 +536,14 @@ interactions: H4sIAAAAAAAAA5VU32/TMBB+319R5YEXWJufg1aagK28sKFVrBMChCInvqihjh059lqo+r9zcZyl EdYEL9Xd+bu77+6+5nA2mXgVKOItJge00WOkUeh5s4rILaiakRyat6ysSnUZJi9EUTSgLn3vVQfn sG/hXDNmIxsJxX8UMJURHyY2v+uAEd8GagmPpdDNuI0SijAMBRZVlPLfiCPvY5vjnQ6Ihb6bgbot - 4GvJU2hyKXZtDz8MozfB664VPlJREQRo2TLwYE+qmsE0F5VdC0I4qaB9XOmMlflkCY/DGyv5th2n + 4GvJU2hyKXZtDz9M3oT+664VPlJREQRo2TLwYE+qmsE0F5VdC0I4qaB9XOmMlflkCY/DGyv5th2n b4ZwseMg01w3SlQg28Trh2D3UHy9XWa327vVDQ0+w+/N1T7zDE87hDmZc+Oz9Yf79fmnVbBb/9ov 33+8uSZfVj+v7uM7VsZ04JJLIApoSszVQz+Iz/3kPIzXfrQILxbhfDqfR3EUfxtSGl3XQqoUcAks JZRKaNpxPPvwzrkQXdPnGkX+NAoiPwkcjeqN4JByXWXdal4GF4mfJEkQRvFAq5aC6lyVgiOXgrAG nu7VS9wc3uxMc5SxYjh4AdCSt2prL0/bWZ5fnylzxN8fRkrjg46UNaWQlcqsZ2ZNq9hT/U1R4yAb JG2Ag+fC0rKptTKssWbvuJC9njro4DmxEmhPFEVhbCeOSGrrGcuFEZLiNIafNV2oHPPTjWAn5azr QuNnRXML7W0X7u9/0uxp8NlhtPQx9OgcRUnCG2Jk1Q00CrgIZIRvU5LnQnN793HElZMTxjKSm6+C - Nxs8FxaV0he2ZvtJOzv+AW6kmdzNBQAA + Nxs8FxaV0he2ZvtJOzv+AfVvz5DNBQAA headers: content-encoding: [gzip] content-length: ['594'] @@ -557,9 +557,9 @@ interactions: response: body: {string: !!python/unicode "{\n \"customers\": [\n {\n \"name\": null,\n \"links\": {\n \"source\": null,\n \"destination\": - null\n },\n \"updated_at\": \"2014-07-22T17:51:58.314446Z\",\n \"created_at\": - \"2014-07-22T17:51:58.205134Z\",\n \"dob_month\": null,\n \"id\": - \"CU6PdAsVriTtB542L7QxJOxn\",\n \"phone\": null,\n \"href\": \"/customers/CU6PdAsVriTtB542L7QxJOxn\",\n + null\n },\n \"updated_at\": \"2014-07-29T09:59:11.504432Z\",\n \"created_at\": + \"2014-07-29T09:59:11.373873Z\",\n \"dob_month\": null,\n \"id\": + \"CU7HQxjNsUWm6TTgylayybfX\",\n \"phone\": null,\n \"href\": \"/customers/CU7HQxjNsUWm6TTgylayybfX\",\n \ \"merchant_status\": \"no-match\",\n \"meta\": {\n \"seq\": \"3\"\n },\n \"dob_year\": null,\n \"address\": {\n \"city\": null,\n \"line2\": null,\n \"line1\": null,\n \"state\": @@ -591,16 +591,16 @@ interactions: \ \"customer\": null\n },\n \"fingerprint\": \"1e2c425a579a1c7f27a037f0662b855035ceadbff18a0adbe785fb07e2afe1cc\",\n \ \"cvv_result\": null,\n \"number\": \"xxxxxxxxxxxx4242\",\n \"avs_postal_match\": null,\n \"expiration_month\": 12,\n \"meta\": {},\n \"id\": - \"CC6Q9AMkJyop1mQoIoOLoUb6\",\n \"category\": \"other\",\n \"type\": + \"CC7J72dswEquYGPf9vnEqJYQ\",\n \"category\": \"other\",\n \"type\": \"credit\",\n \"cvv_match\": null,\n \"bank_name\": \"\",\n \"avs_street_match\": - null,\n \"brand\": \"Visa\",\n \"updated_at\": \"2014-07-22T17:51:59.036048Z\",\n + null,\n \"brand\": \"Visa\",\n \"updated_at\": \"2014-07-29T09:59:12.505259Z\",\n \ \"address\": {\n \"city\": null,\n \"line2\": null,\n \ \"line1\": null,\n \"state\": null,\n \"postal_code\": null,\n \"country_code\": null\n },\n \"can_debit\": true,\n \ \"name\": null,\n \"expiration_year\": 2020,\n \"cvv\": null,\n \ \"is_verified\": true,\n \"avs_result\": null,\n \"can_credit\": - false,\n \"href\": \"/cards/CC6Q9AMkJyop1mQoIoOLoUb6\",\n \"created_at\": - \"2014-07-22T17:51:59.036045Z\"\n }\n ],\n \"links\": {\n \"cards.card_holds\": + false,\n \"href\": \"/cards/CC7J72dswEquYGPf9vnEqJYQ\",\n \"created_at\": + \"2014-07-29T09:59:12.505257Z\"\n }\n ],\n \"links\": {\n \"cards.card_holds\": \"/cards/{cards.id}/card_holds\",\n \"cards.customer\": \"/customers/{cards.customer}\",\n \ \"cards.disputes\": \"/cards/{cards.id}/disputes\",\n \"cards.debits\": \"/cards/{cards.id}/debits\"\n }\n}"} @@ -612,106 +612,106 @@ interactions: content-type: [application/json] status: {code: 201, message: CREATED} - request: - body: '{"links": {"customer": "/customers/CU6PdAsVriTtB542L7QxJOxn"}, "cvv_result": + body: '{"links": {"customer": "/customers/CU7HQxjNsUWm6TTgylayybfX"}, "cvv_result": null, "number": "xxxxxxxxxxxx4242", "expiration_month": 12, "meta": {}, "id": - "CC6Q9AMkJyop1mQoIoOLoUb6", "category": "other", "is_verified": true, "type": + "CC7J72dswEquYGPf9vnEqJYQ", "category": "other", "is_verified": true, "type": "credit", "cvv_match": null, "bank_name": "", "avs_street_match": null, "brand": - "Visa", "updated_at": "2014-07-22T17:51:59.036048Z", "fingerprint": "1e2c425a579a1c7f27a037f0662b855035ceadbff18a0adbe785fb07e2afe1cc", + "Visa", "updated_at": "2014-07-29T09:59:12.505259Z", "fingerprint": "1e2c425a579a1c7f27a037f0662b855035ceadbff18a0adbe785fb07e2afe1cc", "can_debit": true, "name": null, "expiration_year": 2020, "can_credit": false, - "avs_postal_match": null, "avs_result": null, "cvv": null, "created_at": "2014-07-22T17:51:59.036045Z", + "avs_postal_match": null, "avs_result": null, "cvv": null, "created_at": "2014-07-29T09:59:12.505257Z", "address": {"city": null, "line2": null, "line1": null, "state": null, "postal_code": null, "country_code": null}}' headers: {} method: PUT - uri: https://api.balancedpayments.com:443/cards/CC6Q9AMkJyop1mQoIoOLoUb6 + uri: https://api.balancedpayments.com:443/cards/CC7J72dswEquYGPf9vnEqJYQ response: body: string: !!binary | - H4sIAAAAAAAAA41UTW+jMBC991cgzm1iHD6S3NqetuqqG6ntoasVMvbQWAEb2SZKFPHf1+YjIWmq - XQ4IZvxm3sw8z+HG83xKFNP+0vttfzzv0L6tueBi48yDwZ2stZElKGv1H9/iX+xevyv+ah6iED8n - q93Ty074Pb65HQLlXHyCqhQXxgEDwDTEEYmSBQlokuOEoFmSozjG2TyK0CyiQFiW58GcIPsByTzK - M5QAJjkElPrHwHS7TRXounBxRV0UR4+oy6yjuRs9IQ7xCU22Oq2kNqRIS2Lo+jIG7CquiOFSpKUU - xvkDfMxQgiGuOacyOWvb8hivFvc/N097WQXlSv6QL8/yLYtHrImBT6n27rQ0a0vzGNTsK3BmqoBx - c17oVY4ZEZtUkLJFnZemjQIw10vLFBEt23euyQlWV8xyYylp54RREN6h5A7j1yBZRsEyWkzCBQoD - /DHKxJidwKVMuHHVjSfS6QnwdXPw1WznYlxVF0H6gVHJrjiprIVR+3Tk/SJGSkTKILPdXXpG1XCS - TNfFMxmNJLAH4mSPEUZj/V0w9LlOt6B4zsH19yyB09t1tTpO/ciXXk4KfWK1VpDbQP60vaTT/1CX - gn/MEM1iFEYf3T1tbIP+uIIurnu3FCYua7qWRbsfBhKHlsuEs6Zl1fu7rgy40aKYDltDT3vkYGh6 - HfUgxnVVG/gm1dF7lqgd5XeIzmeLa26av4tyJa7qBAAA + H4sIAAAAAAAAA41UXW+bMBR9769APK8JuCEOea2qTX2YVind1k4TMvZ14hUMtU0WFPHfa/ORkDTV + xkNE7uXce+49x95feZ5PiWLaX3q/7B/P27e/NpwJ+eLCQ8B9WWlT5KBs1L99xF8edn++6scf+Xy1 + WtcZqeuU//R7fPNpKMSFXIMqlZDGAUNAdIYiEuGYhBRzhElwg3kwn6N0EUXBTUSBsJTzcEEC+wJ4 + EfE0wIAIh5BS/1CYbreJAl1lrq6ssuyQkVWedjR3o2eGZuiIJludlIU2JEtyYujmvAbsSqGIEYVM + 8kIalw/RoUMOhrjlHMcUrF3LLb7HiOm/d6/V0+dvPN7Ku9f7p4cRa2JgXajafV2YjaV5KGrqElyY + KmDCnA56kWNK5EsiSd6iTkfTRgGYy6OlisiW7XehyRFWlcxyYwlpdUJBOLsO8DWKV0G8jOJliCYL + jCKMn0edGLMKnNtEGDfdWJHOT4Auh8P3YauLcVOdFekFowW7kKRFJY2qk1H2nRkpkQmD1G536RlV + wdEy3RZPbDSyQA3E2R4FKBj774yhL3SyBSW4ALffkwbOb5fd6jj1ki89TjJ9ZLVRwG0hf9oe0ul/ + uEvBPzSMgsiK+Nyd08Yu6Lcb6Oy4d5fCxHVNNkXW3g8DiX3LZSJY07Lq891WBtzoopgOt4ae9sgh + 0PQ+6kFM6LIy8EGrQ/akUSvlR4guZ4drrpo3WxdHCuoEAAA= headers: content-encoding: [gzip] - content-length: ['546'] + content-length: ['548'] content-type: [application/json] status: {code: 200, message: OK} - request: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU6PdAsVriTtB542L7QxJOxn + uri: https://api.balancedpayments.com:443/customers/CU7HQxjNsUWm6TTgylayybfX response: body: string: !!binary | - H4sIAAAAAAAAA41UTY+bMBC9769AnDdLIGRT5dbtrarUVtr20KpCE9sV1oKd+qNKFPHfO06AOJiu - 98Jh5r1nz5uHT3dJkhKrjWyZ0uk2+YmFJDmdv9gS0DKsCts090Ot4eLFQQcQwrS0ikyBWKdMGy7A - cCl6lV6kG9XsnoJhtAKDiLRY5uViuVkUxXO+2a7z7frdwyovy/LxRzpSiGIRSrFc56vSo1C5q1op - TD0dhlN37Idvj1/oe/1d8WfztC6LT5uvh4+fD+J65r6WYjpgWiv229Gz0cEsroRGkxqEqbQBY52R - qZCLFgypr8e1zMDEYvbHQVdp4KCb7chATUcDShXTk00Rbo4TIO4JV8qK+XIelt3Fp16gyF5io6mI - pDNNIq0w6uh3g0F2VuM9tK7mUqe1qBrQppyOyVrgTVDkN4Hr8KxfLj9uUi+81+g/jBFOM3TtnGed - ncbF9v2uX5FHJKBoVcuGnlfpRcEjc9plHu6SZE9jB+KlAnI2KSJzCw2UKNvxmESPmeH6f+t/fPB+ - 6Rkz2MEwJTAFbxsmhAeXcrZFLLlAAiblem8Ni5BHVMA3CoQG4h6viMYNMtDBR8KK2AwDaIb9F19m - aCJXUGyABQpS0cvb/ko4e0zAxaeWRvM0gPD/6u66f6HHamtTBgAA + H4sIAAAAAAAAA41UwY6bMBC971cgzs2ShGzT5LqXnipVyqpVqwoNtndxF+zUNtWiiH/vOAHi2LTu + hcPMe8+eNw+f7pIkJa02smFKp/vkOxaS5HT+YktAw7Aq2rp+N9ZqLl4tdAQhTMtWER+Idcq04QIM + l2JQGUT6Sa09UjCMFmAQka6Xq81iuV2sd4flbv+w269W9w/LzSZff0snClEsQsm3+Ydt7lCoLItG + ClP5w3Bqj3182n78/Pbzk3760rw/HF66GrqufP56PfNYSeEPmFaKPVt6NjmYxZXQaFKBMIU2YFpr + ZCrkogFDqutxDTPgWcx+WWieBg7a2ToGyh8NKFVMe5si3HQeEPeEK2Xr+fIqLNuL+16gyFFioy6I + pDNNIlthVOd2g0HKVuM9tC7mUqe1KGrQZuOPyRrgdVDkN4Hr8awfNj92Uie81+jfTxFOM3TtnGed + nabFDv1+WJFDJKBoUcmanlfpRMEhc9pnDu6SZEejBPFaADmbFJG5hQZKlJU8JjFgZrju3/oXH5xf + esYM9maYEpiC/xsmhAeXsrZFLLlAAibl+tgaFiFPqIBvFAgNxD5eEY0bZKCDj0QrYjOMoBn2b3yZ + oY5cQbERFihIRS9v+z/COWACLj61NJqnEYT/V3/X/wHgkelOUwYAAA== headers: content-encoding: [gzip] - content-length: ['495'] + content-length: ['496'] content-type: [application/json] status: {code: 200, message: OK} - request: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU6PdAsVriTtB542L7QxJOxn/cards?is_valid=True&limit=10&offset=0 + uri: https://api.balancedpayments.com:443/customers/CU7HQxjNsUWm6TTgylayybfX/cards?is_valid=True&limit=10&offset=0 response: body: string: !!binary | - H4sIAAAAAAAAA61UwU7jMBC98xWRD5wW6pikoZXQiuW0iBWLVDiAVpETT6hFYke2U7VC/fe1naRJ - S9HugR6qdsYz82bem3k/CQKUU8U0mgcv9k8QvPtvay65eHPm3uBeNtrICpS1opvH6W92rZ8UX5gf - cUTukof17f1aoC5++61PVHDxCqpWXBgXGALJIxLTOJnRME8KklB8kRR4OiXZZRzjizgHyrKiCC8p - tj8guYyLDCdAaAFhnqNd4ny1ShXopnR5RVOWO49oqqyFuR59IhKRIZqudFpLbWiZVtTky8McsK65 - ooZLkVZSGOcPya5CBYa64QxtcubHcjN9mF3/ervdyDqsHuRPeX8nH7PpCDU18CrVxr2WZmlh7pKa - TQ3OnCtg3Ow3ehRjRsVbKmjlo/Zb00YBmOOtZYoKj/aJazqENTWz2FhKPU8Eh9EZTs4IWYTJPA7n - 8ew8muEoJM+jSoxZBg5lwo3rbsxIqycgx83hR7PlxbiuDpJ0hOWSHXHmshFGbdKR94MYcypSBpmd - 7jwwqoFBMu0U92Q0ksAGqJM9wQSP9XeAEHGdrkDxgoOb714Bp7fjanWYOsrnQUFLPaBaKihsIjTx - Szr5D3Up+AeH+GKKo/i53dOtHdAf1xDqBe0nhkqqvQgm/crb2p8sfAvtu+uclpxdLexUT0tecXMV - 4lNZFBrMFe4kgwSs97cVDS121+WrSnkMtomwYwy1WKylN9QKVlw2Tr4D78hIexNcXEs0Krj66mHY - IfvDcXBk21N8zriuGwMOVk/8u+f/nLPtZOft4LWe8WkekdbF9d5tT0MXZJOmS1n683+klHvV+feK - +QX6JKbzuQ5Ptn8B8A8qo2AGAAA= + H4sIAAAAAAAAA61UwW7bMAy99ysMHXpaG1uN6yRAsENRbOhhWIF0WzsMhmxRiVZbdiU5ixHk3yfJ + duykKbZDcwgSUiQf+R65PfM8lBJJFZp5P80fz9u6b2POuHi25s5gX1ZKFzlIY0U3D9Hn+83vL+rh + e369WCzrjNR1wn6gNn73oUvEuFiCLCUX2gYGgNMxDkkYTUmQRgxHxL+KmH99jZNJGPpXYQqEJowF + E+KbHxBNQpb4EWDCIEhTtE+crtexBFVlNq+osmzvEVWeNDA3g88Yj3EfTdYqLgulSRbnRKer4xyw + KbkkmhcizguhrT/A+wo5aGKH07fJqRvLTXQXYar+3L5Uj5++sula3L7cPd4PUBMNy0LW9nWhVwbm + PqmuS7DmVALl+rDRkxgTIp5jQXIXddia0hJAn24tkUQ4tN+4In1YVVKDjcbE8YT9YHzhRxd4uvCn + s3A6C/DlJMJhFD0NKlFqGDiWCde2uyEjjZ4AnzYHr82GF227OkrSEpYW9IQzLSqhZR0PvK/EmBIR + U0jMdGeelhX0kmmmeCCjgQRqIFb22Mf+UH9HCBFX8RokZxzsfA8KWL2dVqvF1FI+8xjJVI9qJYGZ + RGjklnT0H+qS8A8OQz80JD41e7ozA/plG0KdoN3EUEaUE8GoW3lT+42Fb6B9tJ2TjNP5wkz1POM5 + 1/PAPy8YU6DnfisZJGBzuK2ob7G9Lu9VymEwTQQtY6jBYiydoZSw5kVl5dvzjnRhboKNa4hGjMv3 + HoYZsjscR0e2OcWXlKuy0mBhdcRvHf+XnO5Ge28Lr/EMT/OAtDau8+46GtogkzReFZk7/ydK2Vet + /6CYW6A3Ylqf7fBs9xe46IvaYAYAAA== headers: content-encoding: [gzip] - content-length: ['647'] + content-length: ['649'] content-type: [application/json] status: {code: 200, message: OK} - request: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU6PdAsVriTtB542L7QxJOxn/cards?is_valid=True&limit=10&offset=0 + uri: https://api.balancedpayments.com:443/customers/CU7HQxjNsUWm6TTgylayybfX/cards?is_valid=True&limit=10&offset=0 response: body: string: !!binary | - H4sIAAAAAAAAA61UwU7jMBC98xWRD5wW6pikoZXQiuW0iBWLVDiAVpETT6hFYke2U7VC/fe1naRJ - S9HugR6qdsYz82bem3k/CQKUU8U0mgcv9k8QvPtvay65eHPm3uBeNtrICpS1opvH6W92rZ8UX5gf - cUTukof17f1aoC5++61PVHDxCqpWXBgXGALJIxLTOJnRME8KklB8kRR4OiXZZRzjizgHyrKiCC8p - tj8guYyLDCdAaAFhnqNd4ny1ShXopnR5RVOWO49oqqyFuR59IhKRIZqudFpLbWiZVtTky8McsK65 - ooZLkVZSGOcPya5CBYa64QxtcubHcjN9mF3/ervdyDqsHuRPeX8nH7PpCDU18CrVxr2WZmlh7pKa - TQ3OnCtg3Ow3ehRjRsVbKmjlo/Zb00YBmOOtZYoKj/aJazqENTWz2FhKPU8Eh9EZTs4IWYTJPA7n - 8ew8muEoJM+jSoxZBg5lwo3rbsxIqycgx83hR7PlxbiuDpJ0hOWSHXHmshFGbdKR94MYcypSBpmd - 7jwwqoFBMu0U92Q0ksAGqJM9wQSP9XeAEHGdrkDxgoOb714Bp7fjanWYOsrnQUFLPaBaKihsIjTx - Szr5D3Up+AeH+GKKo/i53dOtHdAf1xDqBe0nhkqqvQgm/crb2p8sfAvtu+uclpxdLexUT0tecXMV - 4lNZFBrMFe4kgwSs97cVDS121+WrSnkMtomwYwy1WKylN9QKVlw2Tr4D78hIexNcXEs0Krj66mHY - IfvDcXBk21N8zriuGwMOVk/8u+f/nLPtZOft4LWe8WkekdbF9d5tT0MXZJOmS1n683+klHvV+feK - +QX6JKbzuQ5Ptn8B8A8qo2AGAAA= + H4sIAAAAAAAAA61UwW7bMAy99ysMHXpaG1uN6yRAsENRbOhhWIF0WzsMhmxRiVZbdiU5ixHk3yfJ + duykKbZDcwgSUiQf+R65PfM8lBJJFZp5P80fz9u6b2POuHi25s5gX1ZKFzlIY0U3D9Hn+83vL+rh + e369WCzrjNR1wn6gNn73oUvEuFiCLCUX2gYGgNMxDkkYTUmQRgxHxL+KmH99jZNJGPpXYQqEJowF + E+KbHxBNQpb4EWDCIEhTtE+crtexBFVlNq+osmzvEVWeNDA3g88Yj3EfTdYqLgulSRbnRKer4xyw + KbkkmhcizguhrT/A+wo5aGKH07fJqRvLTXQXYar+3L5Uj5++sula3L7cPd4PUBMNy0LW9nWhVwbm + PqmuS7DmVALl+rDRkxgTIp5jQXIXddia0hJAn24tkUQ4tN+4In1YVVKDjcbE8YT9YHzhRxd4uvCn + s3A6C/DlJMJhFD0NKlFqGDiWCde2uyEjjZ4AnzYHr82GF227OkrSEpYW9IQzLSqhZR0PvK/EmBIR + U0jMdGeelhX0kmmmeCCjgQRqIFb22Mf+UH9HCBFX8RokZxzsfA8KWL2dVqvF1FI+8xjJVI9qJYGZ + RGjklnT0H+qS8A8OQz80JD41e7ozA/plG0KdoN3EUEaUE8GoW3lT+42Fb6B9tJ2TjNP5wkz1POM5 + 1/PAPy8YU6DnfisZJGBzuK2ob7G9Lu9VymEwTQQtY6jBYiydoZSw5kVl5dvzjnRhboKNa4hGjMv3 + HoYZsjscR0e2OcWXlKuy0mBhdcRvHf+XnO5Ge28Lr/EMT/OAtDau8+46GtogkzReFZk7/ydK2Vet + /6CYW6A3Ylqf7fBs9xe46IvaYAYAAA== headers: content-encoding: [gzip] - content-length: ['647'] + content-length: ['649'] content-type: [application/json] status: {code: 200, message: OK} version: 1 diff --git a/tests/py/fixtures/TestBillingAssociate.yml b/tests/py/fixtures/TestBillingAssociate.yml index 6cd1fc2d39..66bdc334e6 100644 --- a/tests/py/fixtures/TestBillingAssociate.yml +++ b/tests/py/fixtures/TestBillingAssociate.yml @@ -3,19 +3,19 @@ interactions: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2R4ZqaRKDZoW1GpfgeKFbr + uri: https://api.balancedpayments.com:443/customers/CU3KoKRb123DPYH3y6rw4OmF response: body: string: !!binary | - H4sIAAAAAAAAA41UQW7bMBC85xWCznVkyS6a+JqiPeQWtCjgohBWJBMRkUiFpIoahv7epS3JNKmU - veiwOzPkzo54vEmSlPTayJYpne6Sn1hIkuPpiy0BLcOq6Jvmw1RruHi10AmEMC17RXwg1inThgsw - XIpRZRQZZrW+o2AYLcEgIi3W+Xa1/rRa59/yfLcpdkV+e/+x2Nzn+3SmEMUilLvNXVFsHAqVVdlK - YWp/GE7tsQ/fi6ft/g2eHj/v5Y/8a/f8wh6/VOpyZldL4Q+Y1oo9W3o2O5jFldBoUoMwpTZgemtk - KuSqBUPqy3EtM+BZzN4sdJ0GDtrZDgyUPxpQqpj2NkW4OXhA3BOulBXL5Tws24v7XqBIJ7HRlETS - hSaRvTDq4HaDQape4z20LpdSp7UoG9Bm64/JWuBNUORXgRvwrF82P3ZSJ7yX6N/OEU4zdO2UZ50d - 58WO/WFckUMkoGhZy4aeVulEwSFzOmQO7pxkR6MC8VoCOZkUkbmGBkqUVTwmMWIWuO7f+o4Pzi+9 - YAb7Y5gSmIL/GyaEB5eytkUsOUMCJuW66w2LkGdUwDcKhAZiH6+IxhUy0MFHohexGSbQAvs3vszQ - RK6g2AQLFKSi57f9H+EcMQEXn1oazdMEwv9ruBn+AkKZuqpTBgAA + H4sIAAAAAAAAA41UTW/cIBS851dYPnfjZe1spFwTVZVyaFS1h7aqLAxERrFhw0fb1cr/vY9d28uC + EypZPjxmBt68gcNVluXEaiN7pnR+l/2EQpYdjn9YErhnUBW26z5MtY6LFwedQADT0ioSAqFOmTZc + YMOlGFVGkWFWszuKDaM1NoDIN2tUrda3qzX6im7uqhK+65vytkTbH/lMIYolKBXalgh5FCqbupfC + tGEznLpt77+Vj/LxS4M25cPT90/lfqv+VJ/7j+c9d60UYYN5q9izoxezg0VaCYwmLRam1gYb64zM + hVz12JD2vF3PDA4sZq8Ous4jB11ve4ZV2BqmVDEdTIpwsw+AMCcYKdssl1FcdgcPvQCRnYSFriaS + LiwSaYVRe381aqSxGs6hdb2UOq1F3WFtqrBN1mPeRUV+EbgB9vrl8uM69cJ7jv71HOG8ANeOedbF + YR7suD6MI/KIBCtat7Kjx1F6UfDInA6Fhzsl2dNosHipMTmalJC5hEZKlDU8JTFiFrj+bX3DB+9K + L5jB/hqmBKTg/5qJ4dGhnG0JS06QiEm53lnDEuQZFfGNwkJj4h6vhMYFMtKBR8KKVA8TaIH9G15m + 3CWOoNgEixSkoqe3/Z1wjpiIC08tTeZpAsH9Gq6Gf35I1iBTBgAA headers: content-encoding: [gzip] content-length: ['495'] @@ -25,19 +25,19 @@ interactions: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2R4ZqaRKDZoW1GpfgeKFbr + uri: https://api.balancedpayments.com:443/customers/CU3KoKRb123DPYH3y6rw4OmF response: body: string: !!binary | - H4sIAAAAAAAAA41UQW7bMBC85xWCznVkyS6a+JqiPeQWtCjgohBWJBMRkUiFpIoahv7epS3JNKmU - veiwOzPkzo54vEmSlPTayJYpne6Sn1hIkuPpiy0BLcOq6Jvmw1RruHi10AmEMC17RXwg1inThgsw - XIpRZRQZZrW+o2AYLcEgIi3W+Xa1/rRa59/yfLcpdkV+e/+x2Nzn+3SmEMUilLvNXVFsHAqVVdlK - YWp/GE7tsQ/fi6ft/g2eHj/v5Y/8a/f8wh6/VOpyZldL4Q+Y1oo9W3o2O5jFldBoUoMwpTZgemtk - KuSqBUPqy3EtM+BZzN4sdJ0GDtrZDgyUPxpQqpj2NkW4OXhA3BOulBXL5Tws24v7XqBIJ7HRlETS - hSaRvTDq4HaDQape4z20LpdSp7UoG9Bm64/JWuBNUORXgRvwrF82P3ZSJ7yX6N/OEU4zdO2UZ50d - 58WO/WFckUMkoGhZy4aeVulEwSFzOmQO7pxkR6MC8VoCOZkUkbmGBkqUVTwmMWIWuO7f+o4Pzi+9 - YAb7Y5gSmIL/GyaEB5eytkUsOUMCJuW66w2LkGdUwDcKhAZiH6+IxhUy0MFHohexGSbQAvs3vszQ - RK6g2AQLFKSi57f9H+EcMQEXn1oazdMEwv9ruBn+AkKZuqpTBgAA + H4sIAAAAAAAAA41UTW/cIBS851dYPnfjZe1spFwTVZVyaFS1h7aqLAxERrFhw0fb1cr/vY9d28uC + EypZPjxmBt68gcNVluXEaiN7pnR+l/2EQpYdjn9YErhnUBW26z5MtY6LFwedQADT0ioSAqFOmTZc + YMOlGFVGkWFWszuKDaM1NoDIN2tUrda3qzX6im7uqhK+65vytkTbH/lMIYolKBXalgh5FCqbupfC + tGEznLpt77+Vj/LxS4M25cPT90/lfqv+VJ/7j+c9d60UYYN5q9izoxezg0VaCYwmLRam1gYb64zM + hVz12JD2vF3PDA4sZq8Ous4jB11ve4ZV2BqmVDEdTIpwsw+AMCcYKdssl1FcdgcPvQCRnYSFriaS + LiwSaYVRe381aqSxGs6hdb2UOq1F3WFtqrBN1mPeRUV+EbgB9vrl8uM69cJ7jv71HOG8ANeOedbF + YR7suD6MI/KIBCtat7Kjx1F6UfDInA6Fhzsl2dNosHipMTmalJC5hEZKlDU8JTFiFrj+bX3DB+9K + L5jB/hqmBKTg/5qJ4dGhnG0JS06QiEm53lnDEuQZFfGNwkJj4h6vhMYFMtKBR8KKVA8TaIH9G15m + 3CWOoNgEixSkoqe3/Z1wjpiIC08tTeZpAsH9Gq6Gf35I1iBTBgAA headers: content-encoding: [gzip] content-length: ['495'] @@ -47,13 +47,13 @@ interactions: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2R4ZqaRKDZoW1GpfgeKFbr/bank_accounts?limit=10&offset=0 + uri: https://api.balancedpayments.com:443/customers/CU3KoKRb123DPYH3y6rw4OmF/bank_accounts?limit=10&offset=0 response: body: {string: !!python/unicode "{\n \"bank_accounts\": [],\n \"meta\": {\n - \ \"last\": \"/customers/CU2R4ZqaRKDZoW1GpfgeKFbr/bank_accounts?limit=10&offset=0\",\n - \ \"next\": null,\n \"href\": \"/customers/CU2R4ZqaRKDZoW1GpfgeKFbr/bank_accounts?limit=10&offset=0\",\n + \ \"last\": \"/customers/CU3KoKRb123DPYH3y6rw4OmF/bank_accounts?limit=10&offset=0\",\n + \ \"next\": null,\n \"href\": \"/customers/CU3KoKRb123DPYH3y6rw4OmF/bank_accounts?limit=10&offset=0\",\n \ \"limit\": 10,\n \"offset\": 0,\n \"previous\": null,\n \"total\": - 0,\n \"first\": \"/customers/CU2R4ZqaRKDZoW1GpfgeKFbr/bank_accounts?limit=10&offset=0\"\n + 0,\n \"first\": \"/customers/CU3KoKRb123DPYH3y6rw4OmF/bank_accounts?limit=10&offset=0\"\n \ },\n \"links\": {}\n}"} headers: content-length: ['396'] @@ -69,8 +69,8 @@ interactions: \"Not Found\",\n \"category_code\": \"not-found\",\n \"description\": \"

The requested URL was not found on the server.

If you entered the URL manually please check your spelling and try again.

Your request id - is OHM698aee0a011311e48ad106429171ffad.\",\n \"status_code\": 404,\n - \ \"category_type\": \"request\",\n \"request_id\": \"OHM698aee0a011311e48ad106429171ffad\"\n + is OHM8c2ebc98013611e4b7da06429171ffad.\",\n \"status_code\": 404,\n + \ \"category_type\": \"request\",\n \"request_id\": \"OHM8c2ebc98013611e4b7da06429171ffad\"\n \ }\n ]\n}"} headers: content-length: ['430'] @@ -87,13 +87,13 @@ interactions: \"321174851\",\n \"bank_name\": \"SAN MATEO CREDIT UNION\",\n \"account_type\": \"checking\",\n \"name\": \"Alice G. Krebs\",\n \"links\": {\n \"customer\": null,\n \"bank_account_verification\": null\n },\n \"can_credit\": - true,\n \"created_at\": \"2014-07-01T11:32:41.951223Z\",\n \"fingerprint\": + true,\n \"created_at\": \"2014-07-01T15:44:12.669501Z\",\n \"fingerprint\": \"5f0ba9fa3f1122ef13b944a40abfe44e7eba9e16934e64200913cb4c402ace14\",\n \"updated_at\": - \"2014-07-01T11:32:41.951226Z\",\n \"href\": \"/bank_accounts/BA3dHpK9BMTGleJbwQyO9U2B\",\n + \"2014-07-01T15:44:12.669504Z\",\n \"href\": \"/bank_accounts/BA4hixLak3IdlPJRaGtMXgB7\",\n \ \"meta\": {},\n \"account_number\": \"xxxxxx0001\",\n \"address\": {\n \"city\": null,\n \"line2\": null,\n \"line1\": null,\n \ \"state\": null,\n \"postal_code\": null,\n \"country_code\": - null\n },\n \"can_debit\": false,\n \"id\": \"BA3dHpK9BMTGleJbwQyO9U2B\"\n + null\n },\n \"can_debit\": false,\n \"id\": \"BA4hixLak3IdlPJRaGtMXgB7\"\n \ }\n ],\n \"links\": {\n \"bank_accounts.credits\": \"/bank_accounts/{bank_accounts.id}/credits\",\n \ \"bank_accounts.bank_account_verifications\": \"/bank_accounts/{bank_accounts.id}/verifications\",\n \ \"bank_accounts.customer\": \"/customers/{bank_accounts.customer}\",\n @@ -111,19 +111,19 @@ interactions: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2R4ZqaRKDZoW1GpfgeKFbr + uri: https://api.balancedpayments.com:443/customers/CU3KoKRb123DPYH3y6rw4OmF response: body: string: !!binary | - H4sIAAAAAAAAA41UQW7bMBC85xWCznVkyS6a+JqiPeQWtCjgohBWJBMRkUiFpIoahv7epS3JNKmU - veiwOzPkzo54vEmSlPTayJYpne6Sn1hIkuPpiy0BLcOq6Jvmw1RruHi10AmEMC17RXwg1inThgsw - XIpRZRQZZrW+o2AYLcEgIi3W+Xa1/rRa59/yfLcpdkV+e/+x2Nzn+3SmEMUilLvNXVFsHAqVVdlK - YWp/GE7tsQ/fi6ft/g2eHj/v5Y/8a/f8wh6/VOpyZldL4Q+Y1oo9W3o2O5jFldBoUoMwpTZgemtk - KuSqBUPqy3EtM+BZzN4sdJ0GDtrZDgyUPxpQqpj2NkW4OXhA3BOulBXL5Tws24v7XqBIJ7HRlETS - hSaRvTDq4HaDQape4z20LpdSp7UoG9Bm64/JWuBNUORXgRvwrF82P3ZSJ7yX6N/OEU4zdO2UZ50d - 58WO/WFckUMkoGhZy4aeVulEwSFzOmQO7pxkR6MC8VoCOZkUkbmGBkqUVTwmMWIWuO7f+o4Pzi+9 - YAb7Y5gSmIL/GyaEB5eytkUsOUMCJuW66w2LkGdUwDcKhAZiH6+IxhUy0MFHohexGSbQAvs3vszQ - RK6g2AQLFKSi57f9H+EcMQEXn1oazdMEwv9ruBn+AkKZuqpTBgAA + H4sIAAAAAAAAA41UTW/cIBS851dYPnfjZe1spFwTVZVyaFS1h7aqLAxERrFhw0fb1cr/vY9d28uC + EypZPjxmBt68gcNVluXEaiN7pnR+l/2EQpYdjn9YErhnUBW26z5MtY6LFwedQADT0ioSAqFOmTZc + YMOlGFVGkWFWszuKDaM1NoDIN2tUrda3qzX6im7uqhK+65vytkTbH/lMIYolKBXalgh5FCqbupfC + tGEznLpt77+Vj/LxS4M25cPT90/lfqv+VJ/7j+c9d60UYYN5q9izoxezg0VaCYwmLRam1gYb64zM + hVz12JD2vF3PDA4sZq8Ous4jB11ve4ZV2BqmVDEdTIpwsw+AMCcYKdssl1FcdgcPvQCRnYSFriaS + LiwSaYVRe381aqSxGs6hdb2UOq1F3WFtqrBN1mPeRUV+EbgB9vrl8uM69cJ7jv71HOG8ANeOedbF + YR7suD6MI/KIBCtat7Kjx1F6UfDInA6Fhzsl2dNosHipMTmalJC5hEZKlDU8JTFiFrj+bX3DB+9K + L5jB/hqmBKTg/5qJ4dGhnG0JS06QiEm53lnDEuQZFfGNwkJj4h6vhMYFMtKBR8KKVA8TaIH9G15m + 3CWOoNgEixSkoqe3/Z1wjpiIC08tTeZpAsH9Gq6Gf35I1iBTBgAA headers: content-encoding: [gzip] content-length: ['495'] @@ -133,19 +133,19 @@ interactions: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2R4ZqaRKDZoW1GpfgeKFbr + uri: https://api.balancedpayments.com:443/customers/CU3KoKRb123DPYH3y6rw4OmF response: body: string: !!binary | - H4sIAAAAAAAAA41UQW7bMBC85xWCznVkyS6a+JqiPeQWtCjgohBWJBMRkUiFpIoahv7epS3JNKmU - veiwOzPkzo54vEmSlPTayJYpne6Sn1hIkuPpiy0BLcOq6Jvmw1RruHi10AmEMC17RXwg1inThgsw - XIpRZRQZZrW+o2AYLcEgIi3W+Xa1/rRa59/yfLcpdkV+e/+x2Nzn+3SmEMUilLvNXVFsHAqVVdlK - YWp/GE7tsQ/fi6ft/g2eHj/v5Y/8a/f8wh6/VOpyZldL4Q+Y1oo9W3o2O5jFldBoUoMwpTZgemtk - KuSqBUPqy3EtM+BZzN4sdJ0GDtrZDgyUPxpQqpj2NkW4OXhA3BOulBXL5Tws24v7XqBIJ7HRlETS - hSaRvTDq4HaDQape4z20LpdSp7UoG9Bm64/JWuBNUORXgRvwrF82P3ZSJ7yX6N/OEU4zdO2UZ50d - 58WO/WFckUMkoGhZy4aeVulEwSFzOmQO7pxkR6MC8VoCOZkUkbmGBkqUVTwmMWIWuO7f+o4Pzi+9 - YAb7Y5gSmIL/GyaEB5eytkUsOUMCJuW66w2LkGdUwDcKhAZiH6+IxhUy0MFHohexGSbQAvs3vszQ - RK6g2AQLFKSi57f9H+EcMQEXn1oazdMEwv9ruBn+AkKZuqpTBgAA + H4sIAAAAAAAAA41UTW/cIBS851dYPnfjZe1spFwTVZVyaFS1h7aqLAxERrFhw0fb1cr/vY9d28uC + EypZPjxmBt68gcNVluXEaiN7pnR+l/2EQpYdjn9YErhnUBW26z5MtY6LFwedQADT0ioSAqFOmTZc + YMOlGFVGkWFWszuKDaM1NoDIN2tUrda3qzX6im7uqhK+65vytkTbH/lMIYolKBXalgh5FCqbupfC + tGEznLpt77+Vj/LxS4M25cPT90/lfqv+VJ/7j+c9d60UYYN5q9izoxezg0VaCYwmLRam1gYb64zM + hVz12JD2vF3PDA4sZq8Ous4jB11ve4ZV2BqmVDEdTIpwsw+AMCcYKdssl1FcdgcPvQCRnYSFriaS + LiwSaYVRe381aqSxGs6hdb2UOq1F3WFtqrBN1mPeRUV+EbgB9vrl8uM69cJ7jv71HOG8ANeOedbF + YR7suD6MI/KIBCtat7Kjx1F6UfDInA6Fhzsl2dNosHipMTmalJC5hEZKlDU8JTFiFrj+bX3DB+9K + L5jB/hqmBKTg/5qJ4dGhnG0JS06QiEm53lnDEuQZFfGNwkJj4h6vhMYFMtKBR8KKVA8TaIH9G15m + 3CWOoNgEixSkoqe3/Z1wjpiIC08tTeZpAsH9Gq6Gf35I1iBTBgAA headers: content-encoding: [gzip] content-length: ['495'] @@ -155,13 +155,13 @@ interactions: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2R4ZqaRKDZoW1GpfgeKFbr/bank_accounts?limit=10&offset=0 + uri: https://api.balancedpayments.com:443/customers/CU3KoKRb123DPYH3y6rw4OmF/bank_accounts?limit=10&offset=0 response: body: {string: !!python/unicode "{\n \"bank_accounts\": [],\n \"meta\": {\n - \ \"last\": \"/customers/CU2R4ZqaRKDZoW1GpfgeKFbr/bank_accounts?limit=10&offset=0\",\n - \ \"next\": null,\n \"href\": \"/customers/CU2R4ZqaRKDZoW1GpfgeKFbr/bank_accounts?limit=10&offset=0\",\n + \ \"last\": \"/customers/CU3KoKRb123DPYH3y6rw4OmF/bank_accounts?limit=10&offset=0\",\n + \ \"next\": null,\n \"href\": \"/customers/CU3KoKRb123DPYH3y6rw4OmF/bank_accounts?limit=10&offset=0\",\n \ \"limit\": 10,\n \"offset\": 0,\n \"previous\": null,\n \"total\": - 0,\n \"first\": \"/customers/CU2R4ZqaRKDZoW1GpfgeKFbr/bank_accounts?limit=10&offset=0\"\n + 0,\n \"first\": \"/customers/CU3KoKRb123DPYH3y6rw4OmF/bank_accounts?limit=10&offset=0\"\n \ },\n \"links\": {}\n}"} headers: content-length: ['396'] @@ -171,70 +171,70 @@ interactions: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/bank_accounts/BA3dHpK9BMTGleJbwQyO9U2B + uri: https://api.balancedpayments.com:443/bank_accounts/BA4hixLak3IdlPJRaGtMXgB7 response: body: string: !!binary | - H4sIAAAAAAAAA5VT0W6bMBR971cgntdgg5OMvCVb1XVVE3VLXzpNyNiX1QoBZMy2KOLfZ1yogaRS - xoMffA7X595z7vHKcdyYZruIMpZXmSrdhfNDXzrO0ZwalnmlRPYryqp9DFLjbuBjPCcfp9j90JFM - jYzuocG/L9fOw3J7s3E+fbv5fLd1ntZ3m7Ult29F6lAYPnsBttNPWEZXaZkKBs7txLmXEJcWT0W2 - a6R2IrVMVpUq3xuBWZWmnbJRf9FvkCIRjCqRZy217aF+a4bRLGISuFCaoWQFFpFAFfCINojrI0yu - 0fwa4S3Gi8BfEDwJp9j3g2crNdGNgSykyMw/0wTFNExokGBNhAQHcUgIJYjGCRACc9Aw4FkYEJgR - H6EQBywmjCCfMsDEFq4KfomWWU/Li4SkEeENLPdWy4B/Ke7D1cP2NoWv8Z/HwyZ88lf2rT0o2ozb - zqjz0Kbir/kQQr1YUM4llGOnhDqcuqQtBf/8NT69LpXu/fS6yDWQRiznZ0ATcHnoo2et5xAb5xOa - ltZ6wZvJvTsqU6nW588mLO4woMMVm7xmq5nK2InjwJiJ4LXXkV8zOKrUpw+ifWHx/jZ06zUWa/fK - 9bolK72R0g6o28yMipiRXqip5f5fv2aWg27GCt8dVe1qw+qr+h9WEmkmDAUAAA== + H4sIAAAAAAAAA5VT0W6bMBR971dYPK+JDU4y8pZuVZVtTacuk6pOEzLm0lghJjJmahTx7zMuzEBS + KePBDz6H63PvOfd4hZAXM7mNGOd5KXXhzdEvc4nQ0Z4GVnmphXyJZLmLQRncC3xCZvTjhHgfWpKt + IdkOavzHYoXuF+vbB/Tp8fbzco1+rpYPK0du3or0YW/5fAN8a55wjLbSIhMc0N0IfVUQFw7PhNzW + UluRRiYvC53vrEBZZlmrbNBf9AeUSAVnWuSyoTY9VP+a4UxGXEEitGFoVYJDFDANScRqxPMxodd4 + do3JmkzmlM6JP5pOwwkmz05qahoDtVdC2n8mKY5ZmLIgJcT3ISVBHFLKKGZxCpTCDAwMZBoGFKbU + xzgkAY8pp9hnHAh1hct9cokW2tGyUZDWIsY9y8c3C7oRr9/YNlgm2fcvj+xO3z+93MzcWzvQrB63 + m1HroUvFq/0wxp1YsCRRUAydEvpw6pKxFPzz1+T0utCm99PrfW6ALOJ5cga0AVeHLnrW+gRi63zK + ssJZL5J6cu+OylaqzPm7DovXD2h/xUZv2aqnMnTi2DNmJJJq3JLfMjio1KX3on1h8e42tOs1FOv2 + yhu3S1aMB0pboGoyMyhiR3qhpob7f/3aWfa6GSp8d1SVZwyrrqq/OOOhGAwFAAA= headers: content-encoding: [gzip] - content-length: ['502'] + content-length: ['503'] content-type: [application/json] status: {code: 200, message: OK} - request: body: '{"routing_number": "321174851", "bank_name": "SAN MATEO CREDIT UNION", "account_type": "checking", "name": "Alice G. Krebs", "links": {"customer": - "/customers/CU2R4ZqaRKDZoW1GpfgeKFbr"}, "can_credit": true, "created_at": "2014-07-01T11:32:41.951223Z", + "/customers/CU3KoKRb123DPYH3y6rw4OmF"}, "can_credit": true, "created_at": "2014-07-01T15:44:12.669501Z", "address": {"city": null, "line2": null, "line1": null, "state": null, "postal_code": - null, "country_code": null}, "updated_at": "2014-07-01T11:32:41.951226Z", "meta": + null, "country_code": null}, "updated_at": "2014-07-01T15:44:12.669504Z", "meta": {}, "account_number": "xxxxxx0001", "fingerprint": "5f0ba9fa3f1122ef13b944a40abfe44e7eba9e16934e64200913cb4c402ace14", - "can_debit": false, "id": "BA3dHpK9BMTGleJbwQyO9U2B"}' + "can_debit": false, "id": "BA4hixLak3IdlPJRaGtMXgB7"}' headers: {} method: PUT - uri: https://api.balancedpayments.com:443/bank_accounts/BA3dHpK9BMTGleJbwQyO9U2B + uri: https://api.balancedpayments.com:443/bank_accounts/BA4hixLak3IdlPJRaGtMXgB7 response: body: string: !!binary | - H4sIAAAAAAAAA5VTW2+bMBR+769APK/EBicZeUvaLuuiJlqWaFKmCRlz3FohwIxZF0X89xkC5dJ0 - 6njwg7/D8Xc553RlGKZPo71HGYuzSKXmxPihLw3jVJ4alnGmRPToRdnBB6lx07ExHpOPQ2x+qIvK - HhE9QIF/my6Nh+nmbmXcrO9u7zfGdnm/WjbF1VueOiZlPXsCttdPNBV1p2koGBhzy1hI8NMGD0W0 - L6jWJDVNlqUqPpwJ3mztNdn9ouvF7S7+jucJf4TFJ1++NOip9n6DFFwwqkQc6bZRFoaVsvxFIqOR - xyQEQukKJTNoEAlUQeDRAjFthMk1Gl8jvMF44tgTgi13iG3b2TUCuJYLMpEiKv8ZcuRTl1OHY10I - HDu+SwgliPocCIExaBjwyHUIjIiNkIsd5hNGkE0ZYNI0zpLgn1wcazR0yQi3uDxJ4AWJQWcQBrOp - E3xOFu7sYTMP4Yv//PW4crf2rHnrAIoWITQe1ck2s/Kn/BBCrWGhQSAh7ecn1LGyvjZWp6SDBvvy - NX59nSqt/fV1Emsg9FgcXADLsZfHNnox+gD8MnlOw7SJXgSFc29aVXbK9fmz0FSoaY1td/Gs82wV - rvSTOHWCsUSQD+ris1W9Tu3yzmi/s3l7G+ql65NtbdugXr100GNaA3k1M70mpaXv5FTV/p/e0suO - mj7DN63KTR1YfpX/BfGP3OgiBQAA + H4sIAAAAAAAAA5VTW2+bMBR+769APK/BBgMjb+llXZY1qbJU2kUVMubQWCEQGdM1ivjvMwTKpenU + 8uAHf4fj73LO4UzT9IAmG58yluaJzPSx9kddatqhOhUs0lzy5NFP8m0AQuG6ZWLsks821j81RVWP + hG6hxH9M5trtZHW90C6X11fTlXY/ny7mbXH9li/3u6qerYFt1BNtRdNpEnMG2s1ImwkIshaPebIp + qTYkFU2WZzLdHgle3luzdLYMsGld3f36au0d8Zcstl9eGgxU+08geMQZlTxNVNskj+NaWfEikdHE + ZwJCLlWFFDm0iAAqIfRpiegmwuQcuecIr7A9JmSMzZHjeDbCv1sBkZILYid4Uv1jRyigXkStCGPT + hAhbgUcIJYgGERACLigYsONZBBxiIuRhiwWEEWRSBpi0jfNd+F8uzgi52LS7XNYCopKE0RsE42JC + 1vz5O91Y0zC++7akN/L25+OF2761BUnLEFqPmmTbWXmuPoRQZ1hoGArIhvlxua+tb4xVKamgwTx9 + jV9fZ1Jpf329SxUQ+ywNT4DV2It9Fz0ZfQhBlXxE46yNnoelc29aVXUq1PlQairVdMa2v3ij42yV + rgyTOPSCGfGwMJrio1WDTt3y3mi/s3l3G5qlG5LtbJvRrF5mDJg2QFHPzKBJZek7OdW1H9NbedlT + M2T4plWFrgIrzop/+IVNBSIFAAA= headers: content-encoding: [gzip] - content-length: ['534'] + content-length: ['533'] content-type: [application/json] status: {code: 200, message: OK} - request: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2R4ZqaRKDZoW1GpfgeKFbr + uri: https://api.balancedpayments.com:443/customers/CU3KoKRb123DPYH3y6rw4OmF response: body: string: !!binary | - H4sIAAAAAAAAA41UQW7bMBC85xWCznVkyS6a+JqiPeQWtCjgohBWJBMRkUiFpIoahv7epS3JNKmU - veiwOzPkzo54vEmSlPTayJYpne6Sn1hIkuPpiy0BLcOq6Jvmw1RruHi10AmEMC17RXwg1inThgsw - XIpRZRQZZrW+o2AYLcEgIi3W+Xa1/rRa59/yfLcpdkV+e/+x2Nzn+3SmEMUilLvNXVFsHAqVVdlK - YWp/GE7tsQ/fi6ft/g2eHj/v5Y/8a/f8wh6/VOpyZldL4Q+Y1oo9W3o2O5jFldBoUoMwpTZgemtk - KuSqBUPqy3EtM+BZzN4sdJ0GDtrZDgyUPxpQqpj2NkW4OXhA3BOulBXL5Tws24v7XqBIJ7HRlETS - hSaRvTDq4HaDQape4z20LpdSp7UoG9Bm64/JWuBNUORXgRvwrF82P3ZSJ7yX6N/OEU4zdO2UZ50d - 58WO/WFckUMkoGhZy4aeVulEwSFzOmQO7pxkR6MC8VoCOZkUkbmGBkqUVTwmMWIWuO7f+o4Pzi+9 - YAb7Y5gSmIL/GyaEB5eytkUsOUMCJuW66w2LkGdUwDcKhAZiH6+IxhUy0MFHohexGSbQAvs3vszQ - RK6g2AQLFKSi57f9H+EcMQEXn1oazdMEwv9ruBn+AkKZuqpTBgAA + H4sIAAAAAAAAA41UTW/cIBS851dYPnfjZe1spFwTVZVyaFS1h7aqLAxERrFhw0fb1cr/vY9d28uC + EypZPjxmBt68gcNVluXEaiN7pnR+l/2EQpYdjn9YErhnUBW26z5MtY6LFwedQADT0ioSAqFOmTZc + YMOlGFVGkWFWszuKDaM1NoDIN2tUrda3qzX6im7uqhK+65vytkTbH/lMIYolKBXalgh5FCqbupfC + tGEznLpt77+Vj/LxS4M25cPT90/lfqv+VJ/7j+c9d60UYYN5q9izoxezg0VaCYwmLRam1gYb64zM + hVz12JD2vF3PDA4sZq8Ous4jB11ve4ZV2BqmVDEdTIpwsw+AMCcYKdssl1FcdgcPvQCRnYSFriaS + LiwSaYVRe381aqSxGs6hdb2UOq1F3WFtqrBN1mPeRUV+EbgB9vrl8uM69cJ7jv71HOG8ANeOedbF + YR7suD6MI/KIBCtat7Kjx1F6UfDInA6Fhzsl2dNosHipMTmalJC5hEZKlDU8JTFiFrj+bX3DB+9K + L5jB/hqmBKTg/5qJ4dGhnG0JS06QiEm53lnDEuQZFfGNwkJj4h6vhMYFMtKBR8KKVA8TaIH9G15m + 3CWOoNgEixSkoqe3/Z1wjpiIC08tTeZpAsH9Gq6Gf35I1iBTBgAA headers: content-encoding: [gzip] content-length: ['495'] @@ -244,21 +244,21 @@ interactions: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2R4ZqaRKDZoW1GpfgeKFbr/bank_accounts?limit=10&offset=0 + uri: https://api.balancedpayments.com:443/customers/CU3KoKRb123DPYH3y6rw4OmF/bank_accounts?limit=10&offset=0 response: body: string: !!binary | - H4sIAAAAAAAAA61U72vbMBD93r/C+MM+rYlkK+lcKCP9sa4LTViXMOgYRpZPrahje7LcNoT875MU - O7bTdHTQfDDk7nT37r3HrQ4cx41o+hBSxrIyVYV77PzSQcdZ2a9Oy6xUIr0L03IRgdR51/cwPiKf - Btj9WBfZHildgMn/GE2c69HsYuqc3VycX82c+eRqOmmKq1mhWua2nt0De9Ajmoq60ygRDJzLnjOW - EBVNPhHpg4Fag9QwWVmobLEBeDb3bsjtH3ozPr/NfuLLnN/B+Esktw12tg4fQQouGFUiS3XbtEyS - arP1dkVG05BJiIXSFUqW0GQkUAVxSE3G9RAmh+joEOEZxse+d0xwLxhgz/NvmwW4XhdkLkVq3ww4 - imjAqc+xLgSO/SgghBJEIw6EwBHoNOBh4BMYEg+hAPssIowgjzLApGlc5vE/sfi94SAgQ9zCci+B - GxD9jhH6pyM//pqPg9Pr2WUC36Kn78tpMPdOm1kLUNSI0HBUK9t45dn+EEIts9A4llDs6ifUsqK+ - JlarpIUGb38YvwwXSu/+MpxnOpGELIv3JK3t5bKd3St9DJFVntOkaKQXsWHuVapsp7X+/jY7uTVh - NuwmtLDi92vrFv3XjNuV5nMiFkKdYPQh47wAdYIqTdwUnk1L498Nie5W3PcbYqfrKRhVMzYodKQO - 5BIeRVYahVtQVKZVMO+qZ1zI9yNAU2ptaBzTOg3d49azIhpYu2ZfdbzfE/G6X9VWYLv59r/O8Xhj - 7/a9qc/aDtT2PWs5ZAdoXbWuHdDFublXbwRVF//fxpbMzj79HYyvkrV2jWoH67+Jyu5WhgYAAA== + H4sIAAAAAAAAA61U227aQBB9z1dYfuhTg3ftBQpSVJFL05QGIkqkXlRZ6/U4rDA2Wq9TEOLfu7vY + 2CZQpVJ4sMTM7MyZc45mc2ZZdkCTuU8ZS/NEZnbf+qWClrUxX5UWaS558uQn+SIAofK252LcJR/a + 2H5fFpkeCV2Azn8bjKz7wfRmbF1Nbq7vptbj6G48qoqLWb5cL009mwGbqxFVRdlpEHMG1m3LGgoI + siof82SuoZYgFUyWZzJd7ABePXrDdDgJsOtdP/z47K074g8ZLz7tGxxs7T+D4BFnVPI0UW2TPI6L + zbb7FRlNfCYg5FJVSJFDlRFAJYQ+1RnbRZico+45wlPc7hPSx26r0+m1Ef5ZLRCpdUEsBU/Mm3aE + AtqLqBdh7LoQYS/oEUIJokEEhEAXVBpwp+cR6BAXoR72WEAYQS5lgEnVOF+G/8TSaaEudtt1LDMB + kQbhNIzgXA7IjK++0rl3F8YPXyb0Vt5/f7rsVrMWIKkWoeKoVLbyysr8EEI1s9AwFJAd6sfluqC+ + JFappIQG93gYvwxnUu3+MrxMVSL2WRoeSRrbi3U9e1T6EAKjfETjrJKeh5q5k1SZTlv1/a13skvC + TNiOaWbEd0rrZs4p4zal+RjzBZcXGL1LoygDeYEKTewEVrql9u+ORHsv7tsNMdPVFIyKGTsUKlIG + lgKeeZprhWtQZKpU0O+KZxEXb0eAotTYUDumdhqax61lRNSwDs2+aXi/xcOtU9QWYJv5+r/G8Xhl + 7/q9Kc/aAdT6Pas55ABoWbUtHdDEubtXrwRVFv/fxobMxj7OAcaTZG1trdrZ9i8rRFE8hgYAAA== headers: content-encoding: [gzip] content-length: ['625'] @@ -268,21 +268,21 @@ interactions: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2R4ZqaRKDZoW1GpfgeKFbr/bank_accounts?limit=10&offset=0 + uri: https://api.balancedpayments.com:443/customers/CU3KoKRb123DPYH3y6rw4OmF/bank_accounts?limit=10&offset=0 response: body: string: !!binary | - H4sIAAAAAAAAA61U72vbMBD93r/C+MM+rYlkK+lcKCP9sa4LTViXMOgYRpZPrahje7LcNoT875MU - O7bTdHTQfDDk7nT37r3HrQ4cx41o+hBSxrIyVYV77PzSQcdZ2a9Oy6xUIr0L03IRgdR51/cwPiKf - Btj9WBfZHildgMn/GE2c69HsYuqc3VycX82c+eRqOmmKq1mhWua2nt0De9Ajmoq60ygRDJzLnjOW - EBVNPhHpg4Fag9QwWVmobLEBeDb3bsjtH3ozPr/NfuLLnN/B+Esktw12tg4fQQouGFUiS3XbtEyS - arP1dkVG05BJiIXSFUqW0GQkUAVxSE3G9RAmh+joEOEZxse+d0xwLxhgz/NvmwW4XhdkLkVq3ww4 - imjAqc+xLgSO/SgghBJEIw6EwBHoNOBh4BMYEg+hAPssIowgjzLApGlc5vE/sfi94SAgQ9zCci+B - GxD9jhH6pyM//pqPg9Pr2WUC36Kn78tpMPdOm1kLUNSI0HBUK9t45dn+EEIts9A4llDs6ifUsqK+ - JlarpIUGb38YvwwXSu/+MpxnOpGELIv3JK3t5bKd3St9DJFVntOkaKQXsWHuVapsp7X+/jY7uTVh - NuwmtLDi92vrFv3XjNuV5nMiFkKdYPQh47wAdYIqTdwUnk1L498Nie5W3PcbYqfrKRhVMzYodKQO - 5BIeRVYahVtQVKZVMO+qZ1zI9yNAU2ptaBzTOg3d49azIhpYu2ZfdbzfE/G6X9VWYLv59r/O8Xhj - 7/a9qc/aDtT2PWs5ZAdoXbWuHdDFublXbwRVF//fxpbMzj79HYyvkrV2jWoH67+Jyu5WhgYAAA== + H4sIAAAAAAAAA61U227aQBB9z1dYfuhTg3ftBQpSVJFL05QGIkqkXlRZ6/U4rDA2Wq9TEOLfu7vY + 2CZQpVJ4sMTM7MyZc45mc2ZZdkCTuU8ZS/NEZnbf+qWClrUxX5UWaS558uQn+SIAofK252LcJR/a + 2H5fFpkeCV2Azn8bjKz7wfRmbF1Nbq7vptbj6G48qoqLWb5cL009mwGbqxFVRdlpEHMG1m3LGgoI + siof82SuoZYgFUyWZzJd7ABePXrDdDgJsOtdP/z47K074g8ZLz7tGxxs7T+D4BFnVPI0UW2TPI6L + zbb7FRlNfCYg5FJVSJFDlRFAJYQ+1RnbRZico+45wlPc7hPSx26r0+m1Ef5ZLRCpdUEsBU/Mm3aE + AtqLqBdh7LoQYS/oEUIJokEEhEAXVBpwp+cR6BAXoR72WEAYQS5lgEnVOF+G/8TSaaEudtt1LDMB + kQbhNIzgXA7IjK++0rl3F8YPXyb0Vt5/f7rsVrMWIKkWoeKoVLbyysr8EEI1s9AwFJAd6sfluqC+ + JFappIQG93gYvwxnUu3+MrxMVSL2WRoeSRrbi3U9e1T6EAKjfETjrJKeh5q5k1SZTlv1/a13skvC + TNiOaWbEd0rrZs4p4zal+RjzBZcXGL1LoygDeYEKTewEVrql9u+ORHsv7tsNMdPVFIyKGTsUKlIG + lgKeeZprhWtQZKpU0O+KZxEXb0eAotTYUDumdhqax61lRNSwDs2+aXi/xcOtU9QWYJv5+r/G8Xhl + 7/q9Kc/aAdT6Pas55ABoWbUtHdDEubtXrwRVFv/fxobMxj7OAcaTZG1trdrZ9i8rRFE8hgYAAA== headers: content-encoding: [gzip] content-length: ['625'] @@ -292,19 +292,19 @@ interactions: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2R4ZqaRKDZoW1GpfgeKFbr + uri: https://api.balancedpayments.com:443/customers/CU3KoKRb123DPYH3y6rw4OmF response: body: string: !!binary | - H4sIAAAAAAAAA41UQW7bMBC85xWCznVkyS6a+JqiPeQWtCjgohBWJBMRkUiFpIoahv7epS3JNKmU - veiwOzPkzo54vEmSlPTayJYpne6Sn1hIkuPpiy0BLcOq6Jvmw1RruHi10AmEMC17RXwg1inThgsw - XIpRZRQZZrW+o2AYLcEgIi3W+Xa1/rRa59/yfLcpdkV+e/+x2Nzn+3SmEMUilLvNXVFsHAqVVdlK - YWp/GE7tsQ/fi6ft/g2eHj/v5Y/8a/f8wh6/VOpyZldL4Q+Y1oo9W3o2O5jFldBoUoMwpTZgemtk - KuSqBUPqy3EtM+BZzN4sdJ0GDtrZDgyUPxpQqpj2NkW4OXhA3BOulBXL5Tws24v7XqBIJ7HRlETS - hSaRvTDq4HaDQape4z20LpdSp7UoG9Bm64/JWuBNUORXgRvwrF82P3ZSJ7yX6N/OEU4zdO2UZ50d - 58WO/WFckUMkoGhZy4aeVulEwSFzOmQO7pxkR6MC8VoCOZkUkbmGBkqUVTwmMWIWuO7f+o4Pzi+9 - YAb7Y5gSmIL/GyaEB5eytkUsOUMCJuW66w2LkGdUwDcKhAZiH6+IxhUy0MFHohexGSbQAvs3vszQ - RK6g2AQLFKSi57f9H+EcMQEXn1oazdMEwv9ruBn+AkKZuqpTBgAA + H4sIAAAAAAAAA41UTW/cIBS851dYPnfjZe1spFwTVZVyaFS1h7aqLAxERrFhw0fb1cr/vY9d28uC + EypZPjxmBt68gcNVluXEaiN7pnR+l/2EQpYdjn9YErhnUBW26z5MtY6LFwedQADT0ioSAqFOmTZc + YMOlGFVGkWFWszuKDaM1NoDIN2tUrda3qzX6im7uqhK+65vytkTbH/lMIYolKBXalgh5FCqbupfC + tGEznLpt77+Vj/LxS4M25cPT90/lfqv+VJ/7j+c9d60UYYN5q9izoxezg0VaCYwmLRam1gYb64zM + hVz12JD2vF3PDA4sZq8Ous4jB11ve4ZV2BqmVDEdTIpwsw+AMCcYKdssl1FcdgcPvQCRnYSFriaS + LiwSaYVRe381aqSxGs6hdb2UOq1F3WFtqrBN1mPeRUV+EbgB9vrl8uM69cJ7jv71HOG8ANeOedbF + YR7suD6MI/KIBCtat7Kjx1F6UfDInA6Fhzsl2dNosHipMTmalJC5hEZKlDU8JTFiFrj+bX3DB+9K + L5jB/hqmBKTg/5qJ4dGhnG0JS06QiEm53lnDEuQZFfGNwkJj4h6vhMYFMtKBR8KKVA8TaIH9G15m + 3CWOoNgEixSkoqe3/Z1wjpiIC08tTeZpAsH9Gq6Gf35I1iBTBgAA headers: content-encoding: [gzip] content-length: ['495'] @@ -314,19 +314,19 @@ interactions: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2R4ZqaRKDZoW1GpfgeKFbr + uri: https://api.balancedpayments.com:443/customers/CU3KoKRb123DPYH3y6rw4OmF response: body: string: !!binary | - H4sIAAAAAAAAA41UQW7bMBC85xWCznVkyS6a+JqiPeQWtCjgohBWJBMRkUiFpIoahv7epS3JNKmU - veiwOzPkzo54vEmSlPTayJYpne6Sn1hIkuPpiy0BLcOq6Jvmw1RruHi10AmEMC17RXwg1inThgsw - XIpRZRQZZrW+o2AYLcEgIi3W+Xa1/rRa59/yfLcpdkV+e/+x2Nzn+3SmEMUilLvNXVFsHAqVVdlK - YWp/GE7tsQ/fi6ft/g2eHj/v5Y/8a/f8wh6/VOpyZldL4Q+Y1oo9W3o2O5jFldBoUoMwpTZgemtk - KuSqBUPqy3EtM+BZzN4sdJ0GDtrZDgyUPxpQqpj2NkW4OXhA3BOulBXL5Tws24v7XqBIJ7HRlETS - hSaRvTDq4HaDQape4z20LpdSp7UoG9Bm64/JWuBNUORXgRvwrF82P3ZSJ7yX6N/OEU4zdO2UZ50d - 58WO/WFckUMkoGhZy4aeVulEwSFzOmQO7pxkR6MC8VoCOZkUkbmGBkqUVTwmMWIWuO7f+o4Pzi+9 - YAb7Y5gSmIL/GyaEB5eytkUsOUMCJuW66w2LkGdUwDcKhAZiH6+IxhUy0MFHohexGSbQAvs3vszQ - RK6g2AQLFKSi57f9H+EcMQEXn1oazdMEwv9ruBn+AkKZuqpTBgAA + H4sIAAAAAAAAA41UTW/cIBS851dYPnfjZe1spFwTVZVyaFS1h7aqLAxERrFhw0fb1cr/vY9d28uC + EypZPjxmBt68gcNVluXEaiN7pnR+l/2EQpYdjn9YErhnUBW26z5MtY6LFwedQADT0ioSAqFOmTZc + YMOlGFVGkWFWszuKDaM1NoDIN2tUrda3qzX6im7uqhK+65vytkTbH/lMIYolKBXalgh5FCqbupfC + tGEznLpt77+Vj/LxS4M25cPT90/lfqv+VJ/7j+c9d60UYYN5q9izoxezg0VaCYwmLRam1gYb64zM + hVz12JD2vF3PDA4sZq8Ous4jB11ve4ZV2BqmVDEdTIpwsw+AMCcYKdssl1FcdgcPvQCRnYSFriaS + LiwSaYVRe381aqSxGs6hdb2UOq1F3WFtqrBN1mPeRUV+EbgB9vrl8uM69cJ7jv71HOG8ANeOedbF + YR7suD6MI/KIBCtat7Kjx1F6UfDInA6Fhzsl2dNosHipMTmalJC5hEZKlDU8JTFiFrj+bX3DB+9K + L5jB/hqmBKTg/5qJ4dGhnG0JS06QiEm53lnDEuQZFfGNwkJj4h6vhMYFMtKBR8KKVA8TaIH9G15m + 3CWOoNgEixSkoqe3/Z1wjpiIC08tTeZpAsH9Gq6Gf35I1iBTBgAA headers: content-encoding: [gzip] content-length: ['495'] @@ -336,13 +336,13 @@ interactions: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2R4ZqaRKDZoW1GpfgeKFbr/cards?limit=10&offset=0 + uri: https://api.balancedpayments.com:443/customers/CU3KoKRb123DPYH3y6rw4OmF/cards?limit=10&offset=0 response: body: {string: !!python/unicode "{\n \"cards\": [],\n \"meta\": {\n \"last\": - \"/customers/CU2R4ZqaRKDZoW1GpfgeKFbr/cards?limit=10&offset=0\",\n \"next\": - null,\n \"href\": \"/customers/CU2R4ZqaRKDZoW1GpfgeKFbr/cards?limit=10&offset=0\",\n + \"/customers/CU3KoKRb123DPYH3y6rw4OmF/cards?limit=10&offset=0\",\n \"next\": + null,\n \"href\": \"/customers/CU3KoKRb123DPYH3y6rw4OmF/cards?limit=10&offset=0\",\n \ \"limit\": 10,\n \"offset\": 0,\n \"previous\": null,\n \"total\": - 0,\n \"first\": \"/customers/CU2R4ZqaRKDZoW1GpfgeKFbr/cards?limit=10&offset=0\"\n + 0,\n \"first\": \"/customers/CU3KoKRb123DPYH3y6rw4OmF/cards?limit=10&offset=0\"\n \ },\n \"links\": {}\n}"} headers: content-length: ['364'] @@ -358,8 +358,8 @@ interactions: \"Not Found\",\n \"category_code\": \"not-found\",\n \"description\": \"

The requested URL was not found on the server.

If you entered the URL manually please check your spelling and try again.

Your request id - is OHM6cbe9978011311e48ad106429171ffad.\",\n \"status_code\": 404,\n - \ \"category_type\": \"request\",\n \"request_id\": \"OHM6cbe9978011311e48ad106429171ffad\"\n + is OHM90c6a126013611e4a4e806429171ffad.\",\n \"status_code\": 404,\n + \ \"category_type\": \"request\",\n \"request_id\": \"OHM90c6a126013611e4a4e806429171ffad\"\n \ }\n ]\n}"} headers: content-length: ['430'] @@ -376,16 +376,16 @@ interactions: \ \"customer\": null\n },\n \"fingerprint\": \"1e2c425a579a1c7f27a037f0662b855035ceadbff18a0adbe785fb07e2afe1cc\",\n \ \"cvv_result\": null,\n \"number\": \"xxxxxxxxxxxx4242\",\n \"avs_postal_match\": null,\n \"expiration_month\": 12,\n \"meta\": {},\n \"id\": - \"CC3k3FMoyWzNKUsXNwXVDxGt\",\n \"category\": \"other\",\n \"type\": + \"CC4q3Nm89zMS5ujKwSr7wduZ\",\n \"category\": \"other\",\n \"type\": \"credit\",\n \"cvv_match\": null,\n \"bank_name\": \"\",\n \"avs_street_match\": - null,\n \"brand\": \"Visa\",\n \"updated_at\": \"2014-07-01T11:32:47.614703Z\",\n + null,\n \"brand\": \"Visa\",\n \"updated_at\": \"2014-07-01T15:44:20.460279Z\",\n \ \"address\": {\n \"city\": null,\n \"line2\": null,\n \ \"line1\": null,\n \"state\": null,\n \"postal_code\": null,\n \"country_code\": null\n },\n \"can_debit\": true,\n \ \"name\": null,\n \"expiration_year\": 2020,\n \"cvv\": null,\n \ \"is_verified\": true,\n \"avs_result\": null,\n \"can_credit\": - false,\n \"href\": \"/cards/CC3k3FMoyWzNKUsXNwXVDxGt\",\n \"created_at\": - \"2014-07-01T11:32:47.614700Z\"\n }\n ],\n \"links\": {\n \"cards.card_holds\": + false,\n \"href\": \"/cards/CC4q3Nm89zMS5ujKwSr7wduZ\",\n \"created_at\": + \"2014-07-01T15:44:20.460277Z\"\n }\n ],\n \"links\": {\n \"cards.card_holds\": \"/cards/{cards.id}/card_holds\",\n \"cards.customer\": \"/customers/{cards.customer}\",\n \ \"cards.disputes\": \"/cards/{cards.id}/disputes\",\n \"cards.debits\": \"/cards/{cards.id}/debits\"\n }\n}"} @@ -400,19 +400,19 @@ interactions: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2R4ZqaRKDZoW1GpfgeKFbr + uri: https://api.balancedpayments.com:443/customers/CU3KoKRb123DPYH3y6rw4OmF response: body: string: !!binary | - H4sIAAAAAAAAA41UQW7bMBC85xWCznVkyS6a+JqiPeQWtCjgohBWJBMRkUiFpIoahv7epS3JNKmU - veiwOzPkzo54vEmSlPTayJYpne6Sn1hIkuPpiy0BLcOq6Jvmw1RruHi10AmEMC17RXwg1inThgsw - XIpRZRQZZrW+o2AYLcEgIi3W+Xa1/rRa59/yfLcpdkV+e/+x2Nzn+3SmEMUilLvNXVFsHAqVVdlK - YWp/GE7tsQ/fi6ft/g2eHj/v5Y/8a/f8wh6/VOpyZldL4Q+Y1oo9W3o2O5jFldBoUoMwpTZgemtk - KuSqBUPqy3EtM+BZzN4sdJ0GDtrZDgyUPxpQqpj2NkW4OXhA3BOulBXL5Tws24v7XqBIJ7HRlETS - hSaRvTDq4HaDQape4z20LpdSp7UoG9Bm64/JWuBNUORXgRvwrF82P3ZSJ7yX6N/OEU4zdO2UZ50d - 58WO/WFckUMkoGhZy4aeVulEwSFzOmQO7pxkR6MC8VoCOZkUkbmGBkqUVTwmMWIWuO7f+o4Pzi+9 - YAb7Y5gSmIL/GyaEB5eytkUsOUMCJuW66w2LkGdUwDcKhAZiH6+IxhUy0MFHohexGSbQAvs3vszQ - RK6g2AQLFKSi57f9H+EcMQEXn1oazdMEwv9ruBn+AkKZuqpTBgAA + H4sIAAAAAAAAA41UTW/cIBS851dYPnfjZe1spFwTVZVyaFS1h7aqLAxERrFhw0fb1cr/vY9d28uC + EypZPjxmBt68gcNVluXEaiN7pnR+l/2EQpYdjn9YErhnUBW26z5MtY6LFwedQADT0ioSAqFOmTZc + YMOlGFVGkWFWszuKDaM1NoDIN2tUrda3qzX6im7uqhK+65vytkTbH/lMIYolKBXalgh5FCqbupfC + tGEznLpt77+Vj/LxS4M25cPT90/lfqv+VJ/7j+c9d60UYYN5q9izoxezg0VaCYwmLRam1gYb64zM + hVz12JD2vF3PDA4sZq8Ous4jB11ve4ZV2BqmVDEdTIpwsw+AMCcYKdssl1FcdgcPvQCRnYSFriaS + LiwSaYVRe381aqSxGs6hdb2UOq1F3WFtqrBN1mPeRUV+EbgB9vrl8uM69cJ7jv71HOG8ANeOedbF + YR7suD6MI/KIBCtat7Kjx1F6UfDInA6Fhzsl2dNosHipMTmalJC5hEZKlDU8JTFiFrj+bX3DB+9K + L5jB/hqmBKTg/5qJ4dGhnG0JS06QiEm53lnDEuQZFfGNwkJj4h6vhMYFMtKBR8KKVA8TaIH9G15m + 3CWOoNgEixSkoqe3/Z1wjpiIC08tTeZpAsH9Gq6Gf35I1iBTBgAA headers: content-encoding: [gzip] content-length: ['495'] @@ -422,19 +422,19 @@ interactions: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2R4ZqaRKDZoW1GpfgeKFbr + uri: https://api.balancedpayments.com:443/customers/CU3KoKRb123DPYH3y6rw4OmF response: body: string: !!binary | - H4sIAAAAAAAAA41UQW7bMBC85xWCznVkyS6a+JqiPeQWtCjgohBWJBMRkUiFpIoahv7epS3JNKmU - veiwOzPkzo54vEmSlPTayJYpne6Sn1hIkuPpiy0BLcOq6Jvmw1RruHi10AmEMC17RXwg1inThgsw - XIpRZRQZZrW+o2AYLcEgIi3W+Xa1/rRa59/yfLcpdkV+e/+x2Nzn+3SmEMUilLvNXVFsHAqVVdlK - YWp/GE7tsQ/fi6ft/g2eHj/v5Y/8a/f8wh6/VOpyZldL4Q+Y1oo9W3o2O5jFldBoUoMwpTZgemtk - KuSqBUPqy3EtM+BZzN4sdJ0GDtrZDgyUPxpQqpj2NkW4OXhA3BOulBXL5Tws24v7XqBIJ7HRlETS - hSaRvTDq4HaDQape4z20LpdSp7UoG9Bm64/JWuBNUORXgRvwrF82P3ZSJ7yX6N/OEU4zdO2UZ50d - 58WO/WFckUMkoGhZy4aeVulEwSFzOmQO7pxkR6MC8VoCOZkUkbmGBkqUVTwmMWIWuO7f+o4Pzi+9 - YAb7Y5gSmIL/GyaEB5eytkUsOUMCJuW66w2LkGdUwDcKhAZiH6+IxhUy0MFHohexGSbQAvs3vszQ - RK6g2AQLFKSi57f9H+EcMQEXn1oazdMEwv9ruBn+AkKZuqpTBgAA + H4sIAAAAAAAAA41UTW/cIBS851dYPnfjZe1spFwTVZVyaFS1h7aqLAxERrFhw0fb1cr/vY9d28uC + EypZPjxmBt68gcNVluXEaiN7pnR+l/2EQpYdjn9YErhnUBW26z5MtY6LFwedQADT0ioSAqFOmTZc + YMOlGFVGkWFWszuKDaM1NoDIN2tUrda3qzX6im7uqhK+65vytkTbH/lMIYolKBXalgh5FCqbupfC + tGEznLpt77+Vj/LxS4M25cPT90/lfqv+VJ/7j+c9d60UYYN5q9izoxezg0VaCYwmLRam1gYb64zM + hVz12JD2vF3PDA4sZq8Ous4jB11ve4ZV2BqmVDEdTIpwsw+AMCcYKdssl1FcdgcPvQCRnYSFriaS + LiwSaYVRe381aqSxGs6hdb2UOq1F3WFtqrBN1mPeRUV+EbgB9vrl8uM69cJ7jv71HOG8ANeOedbF + YR7suD6MI/KIBCtat7Kjx1F6UfDInA6Fhzsl2dNosHipMTmalJC5hEZKlDU8JTFiFrj+bX3DB+9K + L5jB/hqmBKTg/5qJ4dGhnG0JS06QiEm53lnDEuQZFfGNwkJj4h6vhMYFMtKBR8KKVA8TaIH9G15m + 3CWOoNgEixSkoqe3/Z1wjpiIC08tTeZpAsH9Gq6Gf35I1iBTBgAA headers: content-encoding: [gzip] content-length: ['495'] @@ -444,13 +444,13 @@ interactions: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2R4ZqaRKDZoW1GpfgeKFbr/cards?limit=10&offset=0 + uri: https://api.balancedpayments.com:443/customers/CU3KoKRb123DPYH3y6rw4OmF/cards?limit=10&offset=0 response: body: {string: !!python/unicode "{\n \"cards\": [],\n \"meta\": {\n \"last\": - \"/customers/CU2R4ZqaRKDZoW1GpfgeKFbr/cards?limit=10&offset=0\",\n \"next\": - null,\n \"href\": \"/customers/CU2R4ZqaRKDZoW1GpfgeKFbr/cards?limit=10&offset=0\",\n + \"/customers/CU3KoKRb123DPYH3y6rw4OmF/cards?limit=10&offset=0\",\n \"next\": + null,\n \"href\": \"/customers/CU3KoKRb123DPYH3y6rw4OmF/cards?limit=10&offset=0\",\n \ \"limit\": 10,\n \"offset\": 0,\n \"previous\": null,\n \"total\": - 0,\n \"first\": \"/customers/CU2R4ZqaRKDZoW1GpfgeKFbr/cards?limit=10&offset=0\"\n + 0,\n \"first\": \"/customers/CU3KoKRb123DPYH3y6rw4OmF/cards?limit=10&offset=0\"\n \ },\n \"links\": {}\n}"} headers: content-length: ['364'] @@ -460,73 +460,73 @@ interactions: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/cards/CC3k3FMoyWzNKUsXNwXVDxGt + uri: https://api.balancedpayments.com:443/cards/CC4q3Nm89zMS5ujKwSr7wduZ response: body: string: !!binary | - H4sIAAAAAAAAA41UwY6bMBC971cgzt3ENhDSXLdqD1X31G5Xu6qQsYeNFTDINmnSiH+vbUgg2URa - Dhae8Zt5M/Psw10QhIwqrsNV8Go3QXDwqzWXQm6c+WhwJ1tt6gqUtcq2LIeT3acjpBDyDVSjhDT2 - SIiBsJgkNEk/U8zSgqQURWmBFguSL5MERQkDyvOiwEuK7A+ky6TIUQqEFoAZC0+B2XabKdBt6eK6 - 1CePbKvcEwp3ky8mMRnRdKuzptaGlllFDVtfxoBdIxQ1opZZVUvj/JicMlRgqGvDWKbgLt/DQ7SJ - vv6o97//PX7/pZ8f/z4/fdl9MxPW1MBbrfbudG3WluYpqNk34MxMARdTiC30Ksecyk0maeVR56Vp - owDMDZii0rN9EpqOsLbhlhvPqJ8TQTi+R+k9wj8xXkVkFaezBY5TFL1MMnFuJ3ApCGFcddOJ9MoB - ct2M35vtXIyr6iLIMDBW8ytOVrfSqH028b4TI6My45Db7q4Co1oYJdN38UxGEwnsgTo9EUTQVH8X - DEOhsy0oUQhw/T1L4PR2Xa2O0zDyVVDQUo+s1goKGyic++s4/4C6FHxkhugl9K3p7PrHFXRxsfvr - P3NZs3Vd+pfgSOLgucwE7zyrwd935YgbnwSLGjZ6PiCPhm7Q0QDiQjetgRupTt6zRH6UtxC9zxbX - 3XX/Afc5O1/UBAAA + H4sIAAAAAAAAA41UwXKbMBC95ysYzo0tZDA21xw77SWdHtLpMIu0xGpAUEk4cT38eyUZG3CcmfjA + mF293fd2nzjeBUHIQHEdZsEv+xIER/+04UrIFxc+B9zJTpumRmWjsquq4WT/5QwphXxG1SohjT0S + RkhZTBNI0i1ELC1pCmSVlmS9psUmScgqYQi8KMtoA8T+wXSTlAVJkUKJEWPhpTDb73OFuqtcXdf6 + kpFdXXhC4dvkF9OYjmjY67xttIEqr8Gw3XUNfGuFAiMamdeNNC4f0UuHGg24MYwyBXf9Hh7iv6vv + 9Wb779tj0v35+vqo0lfePU1Yg8HnRh3c6cbsLM1LUXNo0YWZQi7MXOhNjgXIl1xC7VFzadooRHNb + WqFAerY/hYYR1rXccuM5+D1REsX3JL0n0Y8oyeI4o2QRrwlNtxMxwLndwLUhhHHqphs5OQfp7XD0 + Pmz3YpyqqyLDwljDbyRZ00mjDvkk+86MDGTOsbDTzQKjOhwtc5rizEYTCxwQnJ8ooWTqvyuGodD5 + HpUoBbr5zho4v912q+M0rDwLSqj0yGqnsLSFwqW/jstPuEvhZ3aYPoV+NL19/naCri726fovXNd8 + 11T+S3AmcfRcFoL3ntWQP03ljBs/CRY1vOjlgDwH+sF6A4gL3XYGP2h1yc4a+VV+hDjlrLj+rv8P + dvtpftQEAAA= headers: content-encoding: [gzip] - content-length: ['525'] + content-length: ['521'] content-type: [application/json] status: {code: 200, message: OK} - request: - body: '{"links": {"customer": "/customers/CU2R4ZqaRKDZoW1GpfgeKFbr"}, "cvv_result": + body: '{"links": {"customer": "/customers/CU3KoKRb123DPYH3y6rw4OmF"}, "cvv_result": null, "number": "xxxxxxxxxxxx4242", "avs_postal_match": null, "expiration_month": - 12, "meta": {}, "id": "CC3k3FMoyWzNKUsXNwXVDxGt", "category": "other", "type": + 12, "meta": {}, "id": "CC4q3Nm89zMS5ujKwSr7wduZ", "category": "other", "type": "credit", "cvv_match": null, "bank_name": "", "avs_street_match": null, "brand": - "Visa", "updated_at": "2014-07-01T11:32:47.614703Z", "fingerprint": "1e2c425a579a1c7f27a037f0662b855035ceadbff18a0adbe785fb07e2afe1cc", + "Visa", "updated_at": "2014-07-01T15:44:20.460279Z", "fingerprint": "1e2c425a579a1c7f27a037f0662b855035ceadbff18a0adbe785fb07e2afe1cc", "can_debit": true, "name": null, "expiration_year": 2020, "can_credit": false, - "is_verified": true, "avs_result": null, "cvv": null, "created_at": "2014-07-01T11:32:47.614700Z", + "is_verified": true, "avs_result": null, "cvv": null, "created_at": "2014-07-01T15:44:20.460277Z", "address": {"city": null, "line2": null, "line1": null, "state": null, "postal_code": null, "country_code": null}}' headers: {} method: PUT - uri: https://api.balancedpayments.com:443/cards/CC3k3FMoyWzNKUsXNwXVDxGt + uri: https://api.balancedpayments.com:443/cards/CC4q3Nm89zMS5ujKwSr7wduZ response: body: string: !!binary | - H4sIAAAAAAAAA41UTW/iMBC991dEOW/BNgmhXFu1B7Q9VNsPUa0ix56AReJkbYeFRfz3tfMBgcJq - c4iSGb+ZNzPPs7vxPJ9RxbU/9T7tj+ft6rc1Z0KunLkzuJOVNkUOylr9+1fyEsx/0ZfZw7x4x09l - uoDZY6L8Fr//1gVKhVyAKpWQxgExEBaQkIbRHcUsSklE0ShK0XhMkkkYolHIgPIkTfGEIvsB0SRM - ExQBoSlgxvxDYLZexwp0lbm4ssqyg0dWedLQ3PSegATkiKZrHZeFNjSLc2rY8jwGbEqhqBGFjPNC - GufH5JAhB0Ndc45lCl635X60Gj1+L7bvf55nr/rj+ffH28PmyfRYUwOLQm3d6cIsLc1DULMtwZmZ - Ai76EFvoRY4JlatY0rxGnZamjQIwV2CKyprtm9D0CKtKbrnxmNZzIggHtyi6RfgHxtMRmQZ3gzBC - 4xDPe5k4txM4l4kwrrr+RBo9Ablsxl/Ndi7GVXUWpB0YK/gFJysqadQ27nm/iJFRGXNIbHennlEV - HCXTdPFERj0JbIE62RNEUF9/Zwx9oeM1KJEKcP09SeD0dlmtjlM78qmX0kwfWS0VpDaQP6wv6fA/ - 1KXgnzOMBmMcRAjNm3u6tw366Qo6u+7NUhi4rPGyyOr90JHY1VwGgu9rVq2/6UqH6y2KYbc19LBF - doZ9q6MWxIUuKwNXUh28J4nqUV5DND5b3P5m/xdacMXM6gQAAA== + H4sIAAAAAAAAA41UXW+bMBR9769APK+JbSDQvHaaJkX70LpVWqcJGfvSeAXDbJM0i/jvs/lISJpO + 4yEi93LuPefeY++vPM9nVHHtL70f9o/n7btfGy6EfHLhMeC+bLSpSlA26t9+C1bV6kuGSfD28/f3 + wW6htuGn8p0/4Ns3Y6FcyEdQtRLSOCAGwkIS0Si+oZjFOYkpCuIcLRYkS6IIBREDyrM8xwlF9gXi + JMozFAOhOWDG/ENhttmkCnRTuLqyKYpDRjZl1tN8njwhCckRTTc6rSttaJGW1LD1eQ14roWiRlQy + LStpXB6TQ4cSDHXDOcoUvBvLbfg7+FgmN38+3EXNr9X2TsVb3jxMWFMDj5Xaua8rs7Y0D0XNrgYX + Zgq4MKdCL3LMqHxKJS071Kk0bRSAuSwtU1R2bO+FpkdYU3PLjae02xNBOLxG8TXCX3G0DMMlCWY4 + SKIwmoihnNsNnNtEGKduupHeT0Auh/HLsN2LcarOigwLYxW/kGRVI43apZPsCzMyKlMOmZ3u0jOq + gaNl+ime2GhigR1QZ3uCCJr674yhL3S6ASVyAW6+Jw2c3y671XEaVr70clroI6u1gtwW8ufdIZ3/ + h7sU/HOHaBYuEInjh/6ctnZAP52gs+PeXwoz1zVdV0V3P4wk9h2XmeBtx2rI91MZcZOLYj7eGno+ + IMdAO1hvAHGh68bAK60O2ZNG3SpfQ/Q5K669av8C0Edhy+oEAAA= headers: content-encoding: [gzip] - content-length: ['550'] + content-length: ['551'] content-type: [application/json] status: {code: 200, message: OK} - request: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2R4ZqaRKDZoW1GpfgeKFbr + uri: https://api.balancedpayments.com:443/customers/CU3KoKRb123DPYH3y6rw4OmF response: body: string: !!binary | - H4sIAAAAAAAAA41UQW7bMBC85xWCznVkyS6a+JqiPeQWtCjgohBWJBMRkUiFpIoahv7epS3JNKmU - veiwOzPkzo54vEmSlPTayJYpne6Sn1hIkuPpiy0BLcOq6Jvmw1RruHi10AmEMC17RXwg1inThgsw - XIpRZRQZZrW+o2AYLcEgIi3W+Xa1/rRa59/yfLcpdkV+e/+x2Nzn+3SmEMUilLvNXVFsHAqVVdlK - YWp/GE7tsQ/fi6ft/g2eHj/v5Y/8a/f8wh6/VOpyZldL4Q+Y1oo9W3o2O5jFldBoUoMwpTZgemtk - KuSqBUPqy3EtM+BZzN4sdJ0GDtrZDgyUPxpQqpj2NkW4OXhA3BOulBXL5Tws24v7XqBIJ7HRlETS - hSaRvTDq4HaDQape4z20LpdSp7UoG9Bm64/JWuBNUORXgRvwrF82P3ZSJ7yX6N/OEU4zdO2UZ50d - 58WO/WFckUMkoGhZy4aeVulEwSFzOmQO7pxkR6MC8VoCOZkUkbmGBkqUVTwmMWIWuO7f+o4Pzi+9 - YAb7Y5gSmIL/GyaEB5eytkUsOUMCJuW66w2LkGdUwDcKhAZiH6+IxhUy0MFHohexGSbQAvs3vszQ - RK6g2AQLFKSi57f9H+EcMQEXn1oazdMEwv9ruBn+AkKZuqpTBgAA + H4sIAAAAAAAAA41UTW/cIBS851dYPnfjZe1spFwTVZVyaFS1h7aqLAxERrFhw0fb1cr/vY9d28uC + EypZPjxmBt68gcNVluXEaiN7pnR+l/2EQpYdjn9YErhnUBW26z5MtY6LFwedQADT0ioSAqFOmTZc + YMOlGFVGkWFWszuKDaM1NoDIN2tUrda3qzX6im7uqhK+65vytkTbH/lMIYolKBXalgh5FCqbupfC + tGEznLpt77+Vj/LxS4M25cPT90/lfqv+VJ/7j+c9d60UYYN5q9izoxezg0VaCYwmLRam1gYb64zM + hVz12JD2vF3PDA4sZq8Ous4jB11ve4ZV2BqmVDEdTIpwsw+AMCcYKdssl1FcdgcPvQCRnYSFriaS + LiwSaYVRe381aqSxGs6hdb2UOq1F3WFtqrBN1mPeRUV+EbgB9vrl8uM69cJ7jv71HOG8ANeOedbF + YR7suD6MI/KIBCtat7Kjx1F6UfDInA6Fhzsl2dNosHipMTmalJC5hEZKlDU8JTFiFrj+bX3DB+9K + L5jB/hqmBKTg/5qJ4dGhnG0JS06QiEm53lnDEuQZFfGNwkJj4h6vhMYFMtKBR8KKVA8TaIH9G15m + 3CWOoNgEixSkoqe3/Z1wjpiIC08tTeZpAsH9Gq6Gf35I1iBTBgAA headers: content-encoding: [gzip] content-length: ['495'] @@ -536,22 +536,22 @@ interactions: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2R4ZqaRKDZoW1GpfgeKFbr/cards?limit=10&offset=0 + uri: https://api.balancedpayments.com:443/customers/CU3KoKRb123DPYH3y6rw4OmF/cards?limit=10&offset=0 response: body: string: !!binary | - H4sIAAAAAAAAA6VUTW/bMAy991cYOuy0JpJix22AYocW7aFYD8X6gQyDIdtUI9SWPFnOkgX575X8 - ETtpOgxbDkFCiuQj3yM3J56HEqbTEs287/aP523qb2vOhHx15s7gXlalUTloa0WXD/Ten/9k97dX - c/VEbgr+ArfXsUZt/PZzl4gL+QK60EIaF0iAJj4NWBCeM5KEnIYMT0KOp1ManwUBngQJsDTmnJwx - bH9AeBbwGIdAGQeSJGiXOFkuIw1llbm8ssqynUdWedzAXA0+PvVpH82WZVSo0rAsyplJFoc5YFUI - zYxQMsqVNM5P6K5CDoa54fRtirQey+XkdXL9Va2fft/dPpTPd7+eH69WN2aAmhl4UXrtXiuzsDB3 - Sc26AGdONKRiGGIbPYoxZvI1kiyvo/ZbK40GMB+EaSZrtI+iZH1YVaQWWxqxmieKiX+Kw1NMvhEy - m9CZfz4KQjwNyHxQKU0tA4cyEcZ1N2Sk0RPQ42by3mx5Ma6rgyQtYYlKjzgTVUmj19HA+06MCZNR - CrGd7swzuoJeMs0U92Q0kMAamJM9xRQP9XeAEIkyWoIWXICb714Bp7fjanWYWspnHmdZ2aNaaOA2 - ERrXSzr+C3Vp+COH4WhK/BDjebOnWzugH64h1Am6nhjKWFmLYNytvK39wcI30L5kIhfmguBPivMS - zAVuRYIkrPb3E/VNtffk35PXVS1Q0rKCmurW0hkKDUuhKifRnltklN17F9eQibjQ/9+wHV19Dg5O - Z3NgR6koi8qAA9LRualZHYl0O955W0CNZ3hwB1S0cZ132426DbJJo4XK6qN+pJR71fr3itVr8UFM - 63MdnmzfAN+7keI2BgAA + H4sIAAAAAAAAA6VUTW/bMAy991cYOuy0JrJsx2mAYocOw4BgH1i3AeswGLJNNVptyZPlpGmR/z5J + tmMnTYdhyyFISJF85Hvk45nnoYyqvEYL77v543mP7tuYCy7urLk32JdNrWUJyljR1ZdgKZefUp8E + rz9+extsZ2oTfijfoC5+97JPxLi4BVUpLrQN9IFkIYloFF9QP4sZiSkOYoZnM5LOowgHUQY0Txnz + 5xSbHxDPI5biGAhl4GcZ2ifO1utEQd0UNq9oimLvEU2ZtjDvR5+QhGSIpus6qWStaZGUVGer4xxw + X3FFNZciKaXQ1u+TfYUSNLXDGdrkuRvLVfgreF/OLx7eXUfNz+XmWsWbvLkZoaYabqXa2tdSrwzM + fVK9rcCaMwU514eNnsSYUnGXCFq6qMPWaq0A9OnWUkWFQ/uV13QIa6rcYMsT6ngi2A/PcXyO/c9+ + tAjDBQkmfjCPwmjUDM1zw8CxTLi23Y0ZafUE5LTZf2o2vGjb1VGSjrBM5iecmWyEVttk5H0ixoyK + JIfUTHfhadXAIJl2igcyGklgC9TKnmCCx/o7Qoh4naxBccbBzveggNXbabVaTB3lC4/Roh5QrRQw + kwhN3ZJO/0JdCv7IIZ6EM0zi+Kbd050Z0A/bEOoF7SaGClo7EUz7lTe1n1n4FtqrgpdcX/r4hWSs + Bn2JO10hAfeH+4mGprp78u/JXVUD1O9YQW11Y+kNlYI1l42V6MAt0tLsvY1ryUSMq/9v2IzOnYOj + 09ke2EnO66rRYIH0dD46Vic830333g5Q6xkf3BEVXVzv3fWj7oJM0mQlC3fUT5Syrzr/QTG3Fs/E + dD7b4dnuN0A3AAY2BgAA headers: content-encoding: [gzip] content-length: ['642'] @@ -561,22 +561,22 @@ interactions: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2R4ZqaRKDZoW1GpfgeKFbr/cards?limit=10&offset=0 + uri: https://api.balancedpayments.com:443/customers/CU3KoKRb123DPYH3y6rw4OmF/cards?limit=10&offset=0 response: body: string: !!binary | - H4sIAAAAAAAAA6VUTW/bMAy991cYOuy0JpJix22AYocW7aFYD8X6gQyDIdtUI9SWPFnOkgX575X8 - ETtpOgxbDkFCiuQj3yM3J56HEqbTEs287/aP523qb2vOhHx15s7gXlalUTloa0WXD/Ten/9k97dX - c/VEbgr+ArfXsUZt/PZzl4gL+QK60EIaF0iAJj4NWBCeM5KEnIYMT0KOp1ManwUBngQJsDTmnJwx - bH9AeBbwGIdAGQeSJGiXOFkuIw1llbm8ssqynUdWedzAXA0+PvVpH82WZVSo0rAsyplJFoc5YFUI - zYxQMsqVNM5P6K5CDoa54fRtirQey+XkdXL9Va2fft/dPpTPd7+eH69WN2aAmhl4UXrtXiuzsDB3 - Sc26AGdONKRiGGIbPYoxZvI1kiyvo/ZbK40GMB+EaSZrtI+iZH1YVaQWWxqxmieKiX+Kw1NMvhEy - m9CZfz4KQjwNyHxQKU0tA4cyEcZ1N2Sk0RPQ42by3mx5Ma6rgyQtYYlKjzgTVUmj19HA+06MCZNR - CrGd7swzuoJeMs0U92Q0kMAamJM9xRQP9XeAEIkyWoIWXICb714Bp7fjanWYWspnHmdZ2aNaaOA2 - ERrXSzr+C3Vp+COH4WhK/BDjebOnWzugH64h1Am6nhjKWFmLYNytvK39wcI30L5kIhfmguBPivMS - zAVuRYIkrPb3E/VNtffk35PXVS1Q0rKCmurW0hkKDUuhKifRnltklN17F9eQibjQ/9+wHV19Dg5O - Z3NgR6koi8qAA9LRualZHYl0O955W0CNZ3hwB1S0cZ132426DbJJo4XK6qN+pJR71fr3itVr8UFM - 63MdnmzfAN+7keI2BgAA + H4sIAAAAAAAAA6VUTW/bMAy991cYOuy0JrJsx2mAYocOw4BgH1i3AeswGLJNNVptyZPlpGmR/z5J + tmMnTYdhyyFISJF85Hvk45nnoYyqvEYL77v543mP7tuYCy7urLk32JdNrWUJyljR1ZdgKZefUp8E + rz9+extsZ2oTfijfoC5+97JPxLi4BVUpLrQN9IFkIYloFF9QP4sZiSkOYoZnM5LOowgHUQY0Txnz + 5xSbHxDPI5biGAhl4GcZ2ifO1utEQd0UNq9oimLvEU2ZtjDvR5+QhGSIpus6qWStaZGUVGer4xxw + X3FFNZciKaXQ1u+TfYUSNLXDGdrkuRvLVfgreF/OLx7eXUfNz+XmWsWbvLkZoaYabqXa2tdSrwzM + fVK9rcCaMwU514eNnsSYUnGXCFq6qMPWaq0A9OnWUkWFQ/uV13QIa6rcYMsT6ngi2A/PcXyO/c9+ + tAjDBQkmfjCPwmjUDM1zw8CxTLi23Y0ZafUE5LTZf2o2vGjb1VGSjrBM5iecmWyEVttk5H0ixoyK + JIfUTHfhadXAIJl2igcyGklgC9TKnmCCx/o7Qoh4naxBccbBzveggNXbabVaTB3lC4/Roh5QrRQw + kwhN3ZJO/0JdCv7IIZ6EM0zi+Kbd050Z0A/bEOoF7SaGClo7EUz7lTe1n1n4FtqrgpdcX/r4hWSs + Bn2JO10hAfeH+4mGprp78u/JXVUD1O9YQW11Y+kNlYI1l42V6MAt0tLsvY1ryUSMq/9v2IzOnYOj + 09ke2EnO66rRYIH0dD46Vic830333g5Q6xkf3BEVXVzv3fWj7oJM0mQlC3fUT5Syrzr/QTG3Fs/E + dD7b4dnuN0A3AAY2BgAA headers: content-encoding: [gzip] content-length: ['642'] diff --git a/tests/py/fixtures/TestBillingClear.yml b/tests/py/fixtures/TestBillingClear.yml index 485e00085a..d8e6763880 100644 --- a/tests/py/fixtures/TestBillingClear.yml +++ b/tests/py/fixtures/TestBillingClear.yml @@ -3,19 +3,19 @@ interactions: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2R4ZqaRKDZoW1GpfgeKFbr + uri: https://api.balancedpayments.com:443/customers/CU3KoKRb123DPYH3y6rw4OmF response: body: string: !!binary | - H4sIAAAAAAAAA41UQW7bMBC85xWCznVkyS6a+JqiPeQWtCjgohBWJBMRkUiFpIoahv7epS3JNKmU - veiwOzPkzo54vEmSlPTayJYpne6Sn1hIkuPpiy0BLcOq6Jvmw1RruHi10AmEMC17RXwg1inThgsw - XIpRZRQZZrW+o2AYLcEgIi3W+Xa1/rRa59/yfLcpdkV+e/+x2Nzn+3SmEMUilLvNXVFsHAqVVdlK - YWp/GE7tsQ/fi6ft/g2eHj/v5Y/8a/f8wh6/VOpyZldL4Q+Y1oo9W3o2O5jFldBoUoMwpTZgemtk - KuSqBUPqy3EtM+BZzN4sdJ0GDtrZDgyUPxpQqpj2NkW4OXhA3BOulBXL5Tws24v7XqBIJ7HRlETS - hSaRvTDq4HaDQape4z20LpdSp7UoG9Bm64/JWuBNUORXgRvwrF82P3ZSJ7yX6N/OEU4zdO2UZ50d - 58WO/WFckUMkoGhZy4aeVulEwSFzOmQO7pxkR6MC8VoCOZkUkbmGBkqUVTwmMWIWuO7f+o4Pzi+9 - YAb7Y5gSmIL/GyaEB5eytkUsOUMCJuW66w2LkGdUwDcKhAZiH6+IxhUy0MFHohexGSbQAvs3vszQ - RK6g2AQLFKSi57f9H+EcMQEXn1oazdMEwv9ruBn+AkKZuqpTBgAA + H4sIAAAAAAAAA41UTW/cIBS851dYPnfjZe1spFwTVZVyaFS1h7aqLAxERrFhw0fb1cr/vY9d28uC + EypZPjxmBt68gcNVluXEaiN7pnR+l/2EQpYdjn9YErhnUBW26z5MtY6LFwedQADT0ioSAqFOmTZc + YMOlGFVGkWFWszuKDaM1NoDIN2tUrda3qzX6im7uqhK+65vytkTbH/lMIYolKBXalgh5FCqbupfC + tGEznLpt77+Vj/LxS4M25cPT90/lfqv+VJ/7j+c9d60UYYN5q9izoxezg0VaCYwmLRam1gYb64zM + hVz12JD2vF3PDA4sZq8Ous4jB11ve4ZV2BqmVDEdTIpwsw+AMCcYKdssl1FcdgcPvQCRnYSFriaS + LiwSaYVRe381aqSxGs6hdb2UOq1F3WFtqrBN1mPeRUV+EbgB9vrl8uM69cJ7jv71HOG8ANeOedbF + YR7suD6MI/KIBCtat7Kjx1F6UfDInA6Fhzsl2dNosHipMTmalJC5hEZKlDU8JTFiFrj+bX3DB+9K + L5jB/hqmBKTg/5qJ4dGhnG0JS06QiEm53lnDEuQZFfGNwkJj4h6vhMYFMtKBR8KKVA8TaIH9G15m + 3CWOoNgEixSkoqe3/Z1wjpiIC08tTeZpAsH9Gq6Gf35I1iBTBgAA headers: content-encoding: [gzip] content-length: ['495'] @@ -25,22 +25,22 @@ interactions: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2R4ZqaRKDZoW1GpfgeKFbr/cards?limit=10&offset=0 + uri: https://api.balancedpayments.com:443/customers/CU3KoKRb123DPYH3y6rw4OmF/cards?limit=10&offset=0 response: body: string: !!binary | - H4sIAAAAAAAAA6VUTW/bMAy991cYOuy0JpJix22AYocW7aFYD8X6gQyDIdtUI9SWPFnOkgX575X8 - ETtpOgxbDkFCiuQj3yM3J56HEqbTEs287/aP523qb2vOhHx15s7gXlalUTloa0WXD/Ten/9k97dX - c/VEbgr+ArfXsUZt/PZzl4gL+QK60EIaF0iAJj4NWBCeM5KEnIYMT0KOp1ManwUBngQJsDTmnJwx - bH9AeBbwGIdAGQeSJGiXOFkuIw1llbm8ssqynUdWedzAXA0+PvVpH82WZVSo0rAsyplJFoc5YFUI - zYxQMsqVNM5P6K5CDoa54fRtirQey+XkdXL9Va2fft/dPpTPd7+eH69WN2aAmhl4UXrtXiuzsDB3 - Sc26AGdONKRiGGIbPYoxZvI1kiyvo/ZbK40GMB+EaSZrtI+iZH1YVaQWWxqxmieKiX+Kw1NMvhEy - m9CZfz4KQjwNyHxQKU0tA4cyEcZ1N2Sk0RPQ42by3mx5Ma6rgyQtYYlKjzgTVUmj19HA+06MCZNR - CrGd7swzuoJeMs0U92Q0kMAamJM9xRQP9XeAEIkyWoIWXICb714Bp7fjanWYWspnHmdZ2aNaaOA2 - ERrXSzr+C3Vp+COH4WhK/BDjebOnWzugH64h1Am6nhjKWFmLYNytvK39wcI30L5kIhfmguBPivMS - zAVuRYIkrPb3E/VNtffk35PXVS1Q0rKCmurW0hkKDUuhKifRnltklN17F9eQibjQ/9+wHV19Dg5O - Z3NgR6koi8qAA9LRualZHYl0O955W0CNZ3hwB1S0cZ132426DbJJo4XK6qN+pJR71fr3itVr8UFM - 63MdnmzfAN+7keI2BgAA + H4sIAAAAAAAAA6VUTW/bMAy991cYOuy0JrJsx2mAYocOw4BgH1i3AeswGLJNNVptyZPlpGmR/z5J + tmMnTYdhyyFISJF85Hvk45nnoYyqvEYL77v543mP7tuYCy7urLk32JdNrWUJyljR1ZdgKZefUp8E + rz9+extsZ2oTfijfoC5+97JPxLi4BVUpLrQN9IFkIYloFF9QP4sZiSkOYoZnM5LOowgHUQY0Txnz + 5xSbHxDPI5biGAhl4GcZ2ifO1utEQd0UNq9oimLvEU2ZtjDvR5+QhGSIpus6qWStaZGUVGer4xxw + X3FFNZciKaXQ1u+TfYUSNLXDGdrkuRvLVfgreF/OLx7eXUfNz+XmWsWbvLkZoaYabqXa2tdSrwzM + fVK9rcCaMwU514eNnsSYUnGXCFq6qMPWaq0A9OnWUkWFQ/uV13QIa6rcYMsT6ngi2A/PcXyO/c9+ + tAjDBQkmfjCPwmjUDM1zw8CxTLi23Y0ZafUE5LTZf2o2vGjb1VGSjrBM5iecmWyEVttk5H0ixoyK + JIfUTHfhadXAIJl2igcyGklgC9TKnmCCx/o7Qoh4naxBccbBzveggNXbabVaTB3lC4/Roh5QrRQw + kwhN3ZJO/0JdCv7IIZ6EM0zi+Kbd050Z0A/bEOoF7SaGClo7EUz7lTe1n1n4FtqrgpdcX/r4hWSs + Bn2JO10hAfeH+4mGprp78u/JXVUD1O9YQW11Y+kNlYI1l42V6MAt0tLsvY1ryUSMq/9v2IzOnYOj + 09ke2EnO66rRYIH0dD46Vic830333g5Q6xkf3BEVXVzv3fWj7oJM0mQlC3fUT5Syrzr/QTG3Fs/E + dD7b4dnuN0A3AAY2BgAA headers: content-encoding: [gzip] content-length: ['642'] @@ -50,7 +50,7 @@ interactions: body: null headers: {} method: DELETE - uri: https://api.balancedpayments.com:443/cards/CC3k3FMoyWzNKUsXNwXVDxGt + uri: https://api.balancedpayments.com:443/cards/CC4q3Nm89zMS5ujKwSr7wduZ response: body: {string: !!python/unicode ''} headers: @@ -61,19 +61,19 @@ interactions: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2R4ZqaRKDZoW1GpfgeKFbr + uri: https://api.balancedpayments.com:443/customers/CU3KoKRb123DPYH3y6rw4OmF response: body: string: !!binary | - H4sIAAAAAAAAA41UQW7bMBC85xWCznVkyS6a+JqiPeQWtCjgohBWJBMRkUiFpIoahv7epS3JNKmU - veiwOzPkzo54vEmSlPTayJYpne6Sn1hIkuPpiy0BLcOq6Jvmw1RruHi10AmEMC17RXwg1inThgsw - XIpRZRQZZrW+o2AYLcEgIi3W+Xa1/rRa59/yfLcpdkV+e/+x2Nzn+3SmEMUilLvNXVFsHAqVVdlK - YWp/GE7tsQ/fi6ft/g2eHj/v5Y/8a/f8wh6/VOpyZldL4Q+Y1oo9W3o2O5jFldBoUoMwpTZgemtk - KuSqBUPqy3EtM+BZzN4sdJ0GDtrZDgyUPxpQqpj2NkW4OXhA3BOulBXL5Tws24v7XqBIJ7HRlETS - hSaRvTDq4HaDQape4z20LpdSp7UoG9Bm64/JWuBNUORXgRvwrF82P3ZSJ7yX6N/OEU4zdO2UZ50d - 58WO/WFckUMkoGhZy4aeVulEwSFzOmQO7pxkR6MC8VoCOZkUkbmGBkqUVTwmMWIWuO7f+o4Pzi+9 - YAb7Y5gSmIL/GyaEB5eytkUsOUMCJuW66w2LkGdUwDcKhAZiH6+IxhUy0MFHohexGSbQAvs3vszQ - RK6g2AQLFKSi57f9H+EcMQEXn1oazdMEwv9ruBn+AkKZuqpTBgAA + H4sIAAAAAAAAA41UTW/cIBS851dYPnfjZe1spFwTVZVyaFS1h7aqLAxERrFhw0fb1cr/vY9d28uC + EypZPjxmBt68gcNVluXEaiN7pnR+l/2EQpYdjn9YErhnUBW26z5MtY6LFwedQADT0ioSAqFOmTZc + YMOlGFVGkWFWszuKDaM1NoDIN2tUrda3qzX6im7uqhK+65vytkTbH/lMIYolKBXalgh5FCqbupfC + tGEznLpt77+Vj/LxS4M25cPT90/lfqv+VJ/7j+c9d60UYYN5q9izoxezg0VaCYwmLRam1gYb64zM + hVz12JD2vF3PDA4sZq8Ous4jB11ve4ZV2BqmVDEdTIpwsw+AMCcYKdssl1FcdgcPvQCRnYSFriaS + LiwSaYVRe381aqSxGs6hdb2UOq1F3WFtqrBN1mPeRUV+EbgB9vrl8uM69cJ7jv71HOG8ANeOedbF + YR7suD6MI/KIBCtat7Kjx1F6UfDInA6Fhzsl2dNosHipMTmalJC5hEZKlDU8JTFiFrj+bX3DB+9K + L5jB/hqmBKTg/5qJ4dGhnG0JS06QiEm53lnDEuQZFfGNwkJj4h6vhMYFMtKBR8KKVA8TaIH9G15m + 3CWOoNgEixSkoqe3/Z1wjpiIC08tTeZpAsH9Gq6Gf35I1iBTBgAA headers: content-encoding: [gzip] content-length: ['495'] @@ -83,13 +83,13 @@ interactions: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2R4ZqaRKDZoW1GpfgeKFbr/cards?limit=10&offset=0 + uri: https://api.balancedpayments.com:443/customers/CU3KoKRb123DPYH3y6rw4OmF/cards?limit=10&offset=0 response: body: {string: !!python/unicode "{\n \"cards\": [],\n \"meta\": {\n \"last\": - \"/customers/CU2R4ZqaRKDZoW1GpfgeKFbr/cards?limit=10&offset=0\",\n \"next\": - null,\n \"href\": \"/customers/CU2R4ZqaRKDZoW1GpfgeKFbr/cards?limit=10&offset=0\",\n + \"/customers/CU3KoKRb123DPYH3y6rw4OmF/cards?limit=10&offset=0\",\n \"next\": + null,\n \"href\": \"/customers/CU3KoKRb123DPYH3y6rw4OmF/cards?limit=10&offset=0\",\n \ \"limit\": 10,\n \"offset\": 0,\n \"previous\": null,\n \"total\": - 0,\n \"first\": \"/customers/CU2R4ZqaRKDZoW1GpfgeKFbr/cards?limit=10&offset=0\"\n + 0,\n \"first\": \"/customers/CU3KoKRb123DPYH3y6rw4OmF/cards?limit=10&offset=0\"\n \ },\n \"links\": {}\n}"} headers: content-length: ['364'] @@ -99,13 +99,13 @@ interactions: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2R4ZqaRKDZoW1GpfgeKFbr/cards?limit=10&offset=0 + uri: https://api.balancedpayments.com:443/customers/CU3KoKRb123DPYH3y6rw4OmF/cards?limit=10&offset=0 response: body: {string: !!python/unicode "{\n \"cards\": [],\n \"meta\": {\n \"last\": - \"/customers/CU2R4ZqaRKDZoW1GpfgeKFbr/cards?limit=10&offset=0\",\n \"next\": - null,\n \"href\": \"/customers/CU2R4ZqaRKDZoW1GpfgeKFbr/cards?limit=10&offset=0\",\n + \"/customers/CU3KoKRb123DPYH3y6rw4OmF/cards?limit=10&offset=0\",\n \"next\": + null,\n \"href\": \"/customers/CU3KoKRb123DPYH3y6rw4OmF/cards?limit=10&offset=0\",\n \ \"limit\": 10,\n \"offset\": 0,\n \"previous\": null,\n \"total\": - 0,\n \"first\": \"/customers/CU2R4ZqaRKDZoW1GpfgeKFbr/cards?limit=10&offset=0\"\n + 0,\n \"first\": \"/customers/CU3KoKRb123DPYH3y6rw4OmF/cards?limit=10&offset=0\"\n \ },\n \"links\": {}\n}"} headers: content-length: ['364'] @@ -115,19 +115,19 @@ interactions: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2R4ZqaRKDZoW1GpfgeKFbr + uri: https://api.balancedpayments.com:443/customers/CU3KoKRb123DPYH3y6rw4OmF response: body: string: !!binary | - H4sIAAAAAAAAA41UQW7bMBC85xWCznVkyS6a+JqiPeQWtCjgohBWJBMRkUiFpIoahv7epS3JNKmU - veiwOzPkzo54vEmSlPTayJYpne6Sn1hIkuPpiy0BLcOq6Jvmw1RruHi10AmEMC17RXwg1inThgsw - XIpRZRQZZrW+o2AYLcEgIi3W+Xa1/rRa59/yfLcpdkV+e/+x2Nzn+3SmEMUilLvNXVFsHAqVVdlK - YWp/GE7tsQ/fi6ft/g2eHj/v5Y/8a/f8wh6/VOpyZldL4Q+Y1oo9W3o2O5jFldBoUoMwpTZgemtk - KuSqBUPqy3EtM+BZzN4sdJ0GDtrZDgyUPxpQqpj2NkW4OXhA3BOulBXL5Tws24v7XqBIJ7HRlETS - hSaRvTDq4HaDQape4z20LpdSp7UoG9Bm64/JWuBNUORXgRvwrF82P3ZSJ7yX6N/OEU4zdO2UZ50d - 58WO/WFckUMkoGhZy4aeVulEwSFzOmQO7pxkR6MC8VoCOZkUkbmGBkqUVTwmMWIWuO7f+o4Pzi+9 - YAb7Y5gSmIL/GyaEB5eytkUsOUMCJuW66w2LkGdUwDcKhAZiH6+IxhUy0MFHohexGSbQAvs3vszQ - RK6g2AQLFKSi57f9H+EcMQEXn1oazdMEwv9ruBn+AkKZuqpTBgAA + H4sIAAAAAAAAA41UTW/cIBS851dYPnfjZe1spFwTVZVyaFS1h7aqLAxERrFhw0fb1cr/vY9d28uC + EypZPjxmBt68gcNVluXEaiN7pnR+l/2EQpYdjn9YErhnUBW26z5MtY6LFwedQADT0ioSAqFOmTZc + YMOlGFVGkWFWszuKDaM1NoDIN2tUrda3qzX6im7uqhK+65vytkTbH/lMIYolKBXalgh5FCqbupfC + tGEznLpt77+Vj/LxS4M25cPT90/lfqv+VJ/7j+c9d60UYYN5q9izoxezg0VaCYwmLRam1gYb64zM + hVz12JD2vF3PDA4sZq8Ous4jB11ve4ZV2BqmVDEdTIpwsw+AMCcYKdssl1FcdgcPvQCRnYSFriaS + LiwSaYVRe381aqSxGs6hdb2UOq1F3WFtqrBN1mPeRUV+EbgB9vrl8uM69cJ7jv71HOG8ANeOedbF + YR7suD6MI/KIBCtat7Kjx1F6UfDInA6Fhzsl2dNosHipMTmalJC5hEZKlDU8JTFiFrj+bX3DB+9K + L5jB/hqmBKTg/5qJ4dGhnG0JS06QiEm53lnDEuQZFfGNwkJj4h6vhMYFMtKBR8KKVA8TaIH9G15m + 3CWOoNgEixSkoqe3/Z1wjpiIC08tTeZpAsH9Gq6Gf35I1iBTBgAA headers: content-encoding: [gzip] content-length: ['495'] @@ -137,21 +137,21 @@ interactions: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2R4ZqaRKDZoW1GpfgeKFbr/bank_accounts?limit=10&offset=0 + uri: https://api.balancedpayments.com:443/customers/CU3KoKRb123DPYH3y6rw4OmF/bank_accounts?limit=10&offset=0 response: body: string: !!binary | - H4sIAAAAAAAAA61U72vbMBD93r/C+MM+rYlkK+lcKCP9sa4LTViXMOgYRpZPrahje7LcNoT875MU - O7bTdHTQfDDk7nT37r3HrQ4cx41o+hBSxrIyVYV77PzSQcdZ2a9Oy6xUIr0L03IRgdR51/cwPiKf - Btj9WBfZHildgMn/GE2c69HsYuqc3VycX82c+eRqOmmKq1mhWua2nt0De9Ajmoq60ygRDJzLnjOW - EBVNPhHpg4Fag9QwWVmobLEBeDb3bsjtH3ozPr/NfuLLnN/B+Esktw12tg4fQQouGFUiS3XbtEyS - arP1dkVG05BJiIXSFUqW0GQkUAVxSE3G9RAmh+joEOEZxse+d0xwLxhgz/NvmwW4XhdkLkVq3ww4 - imjAqc+xLgSO/SgghBJEIw6EwBHoNOBh4BMYEg+hAPssIowgjzLApGlc5vE/sfi94SAgQ9zCci+B - GxD9jhH6pyM//pqPg9Pr2WUC36Kn78tpMPdOm1kLUNSI0HBUK9t45dn+EEIts9A4llDs6ifUsqK+ - JlarpIUGb38YvwwXSu/+MpxnOpGELIv3JK3t5bKd3St9DJFVntOkaKQXsWHuVapsp7X+/jY7uTVh - NuwmtLDi92vrFv3XjNuV5nMiFkKdYPQh47wAdYIqTdwUnk1L498Nie5W3PcbYqfrKRhVMzYodKQO - 5BIeRVYahVtQVKZVMO+qZ1zI9yNAU2ptaBzTOg3d49azIhpYu2ZfdbzfE/G6X9VWYLv59r/O8Xhj - 7/a9qc/aDtT2PWs5ZAdoXbWuHdDFublXbwRVF//fxpbMzj79HYyvkrV2jWoH67+Jyu5WhgYAAA== + H4sIAAAAAAAAA61U227aQBB9z1dYfuhTg3ftBQpSVJFL05QGIkqkXlRZ6/U4rDA2Wq9TEOLfu7vY + 2CZQpVJ4sMTM7MyZc45mc2ZZdkCTuU8ZS/NEZnbf+qWClrUxX5UWaS558uQn+SIAofK252LcJR/a + 2H5fFpkeCV2Azn8bjKz7wfRmbF1Nbq7vptbj6G48qoqLWb5cL009mwGbqxFVRdlpEHMG1m3LGgoI + siof82SuoZYgFUyWZzJd7ABePXrDdDgJsOtdP/z47K074g8ZLz7tGxxs7T+D4BFnVPI0UW2TPI6L + zbb7FRlNfCYg5FJVSJFDlRFAJYQ+1RnbRZico+45wlPc7hPSx26r0+m1Ef5ZLRCpdUEsBU/Mm3aE + AtqLqBdh7LoQYS/oEUIJokEEhEAXVBpwp+cR6BAXoR72WEAYQS5lgEnVOF+G/8TSaaEudtt1LDMB + kQbhNIzgXA7IjK++0rl3F8YPXyb0Vt5/f7rsVrMWIKkWoeKoVLbyysr8EEI1s9AwFJAd6sfluqC+ + JFappIQG93gYvwxnUu3+MrxMVSL2WRoeSRrbi3U9e1T6EAKjfETjrJKeh5q5k1SZTlv1/a13skvC + TNiOaWbEd0rrZs4p4zal+RjzBZcXGL1LoygDeYEKTewEVrql9u+ORHsv7tsNMdPVFIyKGTsUKlIG + lgKeeZprhWtQZKpU0O+KZxEXb0eAotTYUDumdhqax61lRNSwDs2+aXi/xcOtU9QWYJv5+r/G8Xhl + 7/q9Kc/aAdT6Pas55ABoWbUtHdDEubtXrwRVFv/fxobMxj7OAcaTZG1trdrZ9i8rRFE8hgYAAA== headers: content-encoding: [gzip] content-length: ['625'] @@ -161,7 +161,7 @@ interactions: body: null headers: {} method: DELETE - uri: https://api.balancedpayments.com:443/bank_accounts/BA3dHpK9BMTGleJbwQyO9U2B + uri: https://api.balancedpayments.com:443/bank_accounts/BA4hixLak3IdlPJRaGtMXgB7 response: body: {string: !!python/unicode ''} headers: @@ -172,19 +172,19 @@ interactions: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2R4ZqaRKDZoW1GpfgeKFbr + uri: https://api.balancedpayments.com:443/customers/CU3KoKRb123DPYH3y6rw4OmF response: body: string: !!binary | - H4sIAAAAAAAAA41UQW7bMBC85xWCznVkyS6a+JqiPeQWtCjgohBWJBMRkUiFpIoahv7epS3JNKmU - veiwOzPkzo54vEmSlPTayJYpne6Sn1hIkuPpiy0BLcOq6Jvmw1RruHi10AmEMC17RXwg1inThgsw - XIpRZRQZZrW+o2AYLcEgIi3W+Xa1/rRa59/yfLcpdkV+e/+x2Nzn+3SmEMUilLvNXVFsHAqVVdlK - YWp/GE7tsQ/fi6ft/g2eHj/v5Y/8a/f8wh6/VOpyZldL4Q+Y1oo9W3o2O5jFldBoUoMwpTZgemtk - KuSqBUPqy3EtM+BZzN4sdJ0GDtrZDgyUPxpQqpj2NkW4OXhA3BOulBXL5Tws24v7XqBIJ7HRlETS - hSaRvTDq4HaDQape4z20LpdSp7UoG9Bm64/JWuBNUORXgRvwrF82P3ZSJ7yX6N/OEU4zdO2UZ50d - 58WO/WFckUMkoGhZy4aeVulEwSFzOmQO7pxkR6MC8VoCOZkUkbmGBkqUVTwmMWIWuO7f+o4Pzi+9 - YAb7Y5gSmIL/GyaEB5eytkUsOUMCJuW66w2LkGdUwDcKhAZiH6+IxhUy0MFHohexGSbQAvs3vszQ - RK6g2AQLFKSi57f9H+EcMQEXn1oazdMEwv9ruBn+AkKZuqpTBgAA + H4sIAAAAAAAAA41UTW/cIBS851dYPnfjZe1spFwTVZVyaFS1h7aqLAxERrFhw0fb1cr/vY9d28uC + EypZPjxmBt68gcNVluXEaiN7pnR+l/2EQpYdjn9YErhnUBW26z5MtY6LFwedQADT0ioSAqFOmTZc + YMOlGFVGkWFWszuKDaM1NoDIN2tUrda3qzX6im7uqhK+65vytkTbH/lMIYolKBXalgh5FCqbupfC + tGEznLpt77+Vj/LxS4M25cPT90/lfqv+VJ/7j+c9d60UYYN5q9izoxezg0VaCYwmLRam1gYb64zM + hVz12JD2vF3PDA4sZq8Ous4jB11ve4ZV2BqmVDEdTIpwsw+AMCcYKdssl1FcdgcPvQCRnYSFriaS + LiwSaYVRe381aqSxGs6hdb2UOq1F3WFtqrBN1mPeRUV+EbgB9vrl8uM69cJ7jv71HOG8ANeOedbF + YR7suD6MI/KIBCtat7Kjx1F6UfDInA6Fhzsl2dNosHipMTmalJC5hEZKlDU8JTFiFrj+bX3DB+9K + L5jB/hqmBKTg/5qJ4dGhnG0JS06QiEm53lnDEuQZFfGNwkJj4h6vhMYFMtKBR8KKVA8TaIH9G15m + 3CWOoNgEixSkoqe3/Z1wjpiIC08tTeZpAsH9Gq6Gf35I1iBTBgAA headers: content-encoding: [gzip] content-length: ['495'] @@ -194,13 +194,13 @@ interactions: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2R4ZqaRKDZoW1GpfgeKFbr/bank_accounts?limit=10&offset=0 + uri: https://api.balancedpayments.com:443/customers/CU3KoKRb123DPYH3y6rw4OmF/bank_accounts?limit=10&offset=0 response: body: {string: !!python/unicode "{\n \"bank_accounts\": [],\n \"meta\": {\n - \ \"last\": \"/customers/CU2R4ZqaRKDZoW1GpfgeKFbr/bank_accounts?limit=10&offset=0\",\n - \ \"next\": null,\n \"href\": \"/customers/CU2R4ZqaRKDZoW1GpfgeKFbr/bank_accounts?limit=10&offset=0\",\n + \ \"last\": \"/customers/CU3KoKRb123DPYH3y6rw4OmF/bank_accounts?limit=10&offset=0\",\n + \ \"next\": null,\n \"href\": \"/customers/CU3KoKRb123DPYH3y6rw4OmF/bank_accounts?limit=10&offset=0\",\n \ \"limit\": 10,\n \"offset\": 0,\n \"previous\": null,\n \"total\": - 0,\n \"first\": \"/customers/CU2R4ZqaRKDZoW1GpfgeKFbr/bank_accounts?limit=10&offset=0\"\n + 0,\n \"first\": \"/customers/CU3KoKRb123DPYH3y6rw4OmF/bank_accounts?limit=10&offset=0\"\n \ },\n \"links\": {}\n}"} headers: content-length: ['396'] @@ -210,13 +210,13 @@ interactions: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2R4ZqaRKDZoW1GpfgeKFbr/bank_accounts?limit=10&offset=0 + uri: https://api.balancedpayments.com:443/customers/CU3KoKRb123DPYH3y6rw4OmF/bank_accounts?limit=10&offset=0 response: body: {string: !!python/unicode "{\n \"bank_accounts\": [],\n \"meta\": {\n - \ \"last\": \"/customers/CU2R4ZqaRKDZoW1GpfgeKFbr/bank_accounts?limit=10&offset=0\",\n - \ \"next\": null,\n \"href\": \"/customers/CU2R4ZqaRKDZoW1GpfgeKFbr/bank_accounts?limit=10&offset=0\",\n + \ \"last\": \"/customers/CU3KoKRb123DPYH3y6rw4OmF/bank_accounts?limit=10&offset=0\",\n + \ \"next\": null,\n \"href\": \"/customers/CU3KoKRb123DPYH3y6rw4OmF/bank_accounts?limit=10&offset=0\",\n \ \"limit\": 10,\n \"offset\": 0,\n \"previous\": null,\n \"total\": - 0,\n \"first\": \"/customers/CU2R4ZqaRKDZoW1GpfgeKFbr/bank_accounts?limit=10&offset=0\"\n + 0,\n \"first\": \"/customers/CU3KoKRb123DPYH3y6rw4OmF/bank_accounts?limit=10&offset=0\"\n \ },\n \"links\": {}\n}"} headers: content-length: ['396'] diff --git a/tests/py/fixtures/TestBillingPayday.yml b/tests/py/fixtures/TestBillingPayday.yml deleted file mode 100644 index 09b174f739..0000000000 --- a/tests/py/fixtures/TestBillingPayday.yml +++ /dev/null @@ -1,148 +0,0 @@ -interactions: -- request: - body: null - headers: {} - method: GET - uri: https://api.balancedpayments.com:443/customers/CU2RCigJCqQDdYQEcThv3Jxr - response: - body: - string: !!binary | - H4sIAAAAAAAAA41Uy27bMBC85ysEnevIkp028NXtJbcU6aEtCmFNshURiXT4CGIY+vcubUmmSaXs - RYfdmSF3dsTjTZblxGojO6Z0vsl+YiHLjqcvtgR0DKvCtu2HsdZy8eygIwhhWlpFQiDWKdOGCzBc - ikFlEOknNbunYBitwSAir5blerH8tFiWT2W5WVWbqrpd35X398sf+UQhiiUoq2p993HlUajc1Z0U - pgmH4dQdu/1Wfd3yPw/bl8fP9PvjF/LUvK4e3tTlzH0jRThg3ij229GLycEirYRGkwaEqbUBY52R - uZCLDgxpLsd1zEBgMXtx0DKPHHSzHRiocDSgVDEdbIpwcwiAuCdcKavmy2VcdhcPvUCRvcRGWxNJ - Z5pEWmHUwe9Gg+ysxntoXc+lTmtRt6DNOhyTdcDbqMivAtfjWb9cftykXngv0b+dIpwX6Nopz7o4 - Tosd+v2wIo9IQNG6kS09rdKLgkfmtC883DnJnsYOxHMN5GRSQuYaGilRtuMpiQEzw/X/1nd88H7p - GTPYm2FKYAr+b5gYHl3K2Zaw5AyJmJTrvTUsQZ5QEd8oEBqIe7wSGlfISAcfCStSM4ygGfYrvszQ - Jq6g2AiLFKSi57f9H+EcMBEXn1qazNMIwv+rv+n/AoxtLhJTBgAA - headers: - content-encoding: [gzip] - content-length: ['495'] - content-type: [application/json] - status: {code: 200, message: OK} -- request: - body: null - headers: {} - method: GET - uri: https://api.balancedpayments.com:443/customers/CU2RCigJCqQDdYQEcThv3Jxr/cards?limit=10&offset=0 - response: - body: - string: !!binary | - H4sIAAAAAAAAA6VVyW7bMBC95ysIHXpKbIlyvAFB4ThBigRx9gJNUQg0F5uITDok5dgI/O8lKcmS - sqBFy4MAzcKZefNm+LoHQICRIjoYgp/2B4BX/7XilIsnJy4FzjLTRi6ostJg/ABvx3x2Pn6+OSE/ - bk7x/XwVn69VUPhv98uLGBczqpaKC+Mc+7jHwg6M4+4hYv0+jmncxSyEvU6XYtTtMIJZ3CUEQxqx - eGDNKR6wmDESHiLY7QW7i/FqlSiqs9TdK7I03WlEtpjmaa5rJ7Kn8kYrnSylNihNFsjguctNyEpP - 10uukOFSJAspjNNH4S7CghrUBEfRmbX10EjBMu1+SnOHHTebxMgXb1FXIEJsETqBTnEs1wC+h5AT - f+8Y3p1dn0yujx9CeXUxRVQPzPO4VhNGhs6k2jhraeYWgl3CZrOkTowVJdw0QSzrb2A4ReIpEWjh - vc6vL69uz0YTMP42ujsFx6PJxT6YtEatJp7aKEpNheeG6spgqpDwdXznGlXibEls1iRBnh0wjDoH - Ye8gjO6jaBjDIYxb/X4fdgaPtVA5ZNa+Tk4LsJXUa8hZTB2yH4gjFy+CMbhEXIA7n3q9MZYaxhf/ - YT8L6mBJvM0ghFFY98YyE0ZtksLAxX83GRiJhNCpbccQGJXRir857I1+1Pi4ocjNIAxhRUg7DG/K - DLhOVlRxxqmDvRHAkX83OkFeOyiYCGybQF4ecNkDIoGQBniW1Brusi/YNAQMpbrKf64oc6i0/W5p - /wVxFf0DCWC/04l7j/lsbC2UvxxaQWMOgxRpz6J2uals7E/2VJ7a15QvuDmKwi+SMU3NUdnDQNB1 - c60EVVHFGvz3y31Um2i5UII8upUUDQ2Wiq64zNwCrlgQGGnXlfPLmRIwrv6/YAujX9ZvNn7+LrQI - 18vM2Dmu2vnqu9riZNveaYuEck39nai1ovArtdtiXIpAzjWZy9S/RSVzaqFq+kYwP0Cf+BQ6V+He - 9jfXVB+z7QYAAA== - headers: - content-encoding: [gzip] - content-length: ['751'] - content-type: [application/json] - status: {code: 200, message: OK} -- request: - body: '{"amount": 1000, "description": "janet"}' - headers: {} - method: POST - uri: https://api.balancedpayments.com:443/cards/CC2SGPDNPBU0oOKbaes9tqC1/debits - response: - body: {string: !!python/unicode "{\n \"debits\": [\n {\n \"status\": - \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"customer\": - \"CU2RCigJCqQDdYQEcThv3Jxr\",\n \"source\": \"CC2SGPDNPBU0oOKbaes9tqC1\",\n - \ \"order\": null,\n \"dispute\": null\n },\n \"updated_at\": - \"2014-07-01T11:33:17.139146Z\",\n \"created_at\": \"2014-07-01T11:33:16.424807Z\",\n - \ \"transaction_number\": \"W408-651-6074\",\n \"failure_reason\": - null,\n \"currency\": \"USD\",\n \"amount\": 1000,\n \"failure_reason_code\": - null,\n \"meta\": {},\n \"href\": \"/debits/WD3QtectdD2zCuJi6l0f8vjj\",\n - \ \"appears_on_statement_as\": \"BAL*example.com\",\n \"id\": \"WD3QtectdD2zCuJi6l0f8vjj\"\n - \ }\n ],\n \"links\": {\n \"debits.customer\": \"/customers/{debits.customer}\",\n - \ \"debits.dispute\": \"/disputes/{debits.dispute}\",\n \"debits.source\": - \"/resources/{debits.source}\",\n \"debits.order\": \"/orders/{debits.order}\",\n - \ \"debits.refunds\": \"/debits/{debits.id}/refunds\",\n \"debits.events\": - \"/debits/{debits.id}/events\"\n }\n}"} - headers: - content-length: ['1002'] - content-type: [application/json] - status: {code: 201, message: CREATED} -- request: - body: null - headers: {} - method: GET - uri: https://api.balancedpayments.com:443/customers/CU2RCigJCqQDdYQEcThv3Jxr - response: - body: - string: !!binary | - H4sIAAAAAAAAA41Uy27bMBC85ysEnevIkp028NXtJbcU6aEtCmFNshURiXT4CGIY+vcubUmmSaXs - RYfdmSF3dsTjTZblxGojO6Z0vsl+YiHLjqcvtgR0DKvCtu2HsdZy8eygIwhhWlpFQiDWKdOGCzBc - ikFlEOknNbunYBitwSAir5blerH8tFiWT2W5WVWbqrpd35X398sf+UQhiiUoq2p993HlUajc1Z0U - pgmH4dQdu/1Wfd3yPw/bl8fP9PvjF/LUvK4e3tTlzH0jRThg3ij229GLycEirYRGkwaEqbUBY52R - uZCLDgxpLsd1zEBgMXtx0DKPHHSzHRiocDSgVDEdbIpwcwiAuCdcKavmy2VcdhcPvUCRvcRGWxNJ - Z5pEWmHUwe9Gg+ysxntoXc+lTmtRt6DNOhyTdcDbqMivAtfjWb9cftykXngv0b+dIpwX6Nopz7o4 - Tosd+v2wIo9IQNG6kS09rdKLgkfmtC883DnJnsYOxHMN5GRSQuYaGilRtuMpiQEzw/X/1nd88H7p - GTPYm2FKYAr+b5gYHl3K2Zaw5AyJmJTrvTUsQZ5QEd8oEBqIe7wSGlfISAcfCStSM4ygGfYrvszQ - Jq6g2AiLFKSi57f9H+EcMBEXn1qazNMIwv+rv+n/AoxtLhJTBgAA - headers: - content-encoding: [gzip] - content-length: ['495'] - content-type: [application/json] - status: {code: 200, message: OK} -- request: - body: null - headers: {} - method: GET - uri: https://api.balancedpayments.com:443/customers/CU2RCigJCqQDdYQEcThv3Jxr/cards?limit=10&offset=0 - response: - body: - string: !!binary | - H4sIAAAAAAAAA6VVyW7bMBC95ysIHXpKbIlyvAFB4ThBigRx9gJNUQg0F5uITDok5dgI/O8lKcmS - sqBFy4MAzcKZefNm+LoHQICRIjoYgp/2B4BX/7XilIsnJy4FzjLTRi6ostJg/ABvx3x2Pn6+OSE/ - bk7x/XwVn69VUPhv98uLGBczqpaKC+Mc+7jHwg6M4+4hYv0+jmncxSyEvU6XYtTtMIJZ3CUEQxqx - eGDNKR6wmDESHiLY7QW7i/FqlSiqs9TdK7I03WlEtpjmaa5rJ7Kn8kYrnSylNihNFsjguctNyEpP - 10uukOFSJAspjNNH4S7CghrUBEfRmbX10EjBMu1+SnOHHTebxMgXb1FXIEJsETqBTnEs1wC+h5AT - f+8Y3p1dn0yujx9CeXUxRVQPzPO4VhNGhs6k2jhraeYWgl3CZrOkTowVJdw0QSzrb2A4ReIpEWjh - vc6vL69uz0YTMP42ujsFx6PJxT6YtEatJp7aKEpNheeG6spgqpDwdXznGlXibEls1iRBnh0wjDoH - Ye8gjO6jaBjDIYxb/X4fdgaPtVA5ZNa+Tk4LsJXUa8hZTB2yH4gjFy+CMbhEXIA7n3q9MZYaxhf/ - YT8L6mBJvM0ghFFY98YyE0ZtksLAxX83GRiJhNCpbccQGJXRir857I1+1Pi4ocjNIAxhRUg7DG/K - DLhOVlRxxqmDvRHAkX83OkFeOyiYCGybQF4ecNkDIoGQBniW1Brusi/YNAQMpbrKf64oc6i0/W5p - /wVxFf0DCWC/04l7j/lsbC2UvxxaQWMOgxRpz6J2uals7E/2VJ7a15QvuDmKwi+SMU3NUdnDQNB1 - c60EVVHFGvz3y31Um2i5UII8upUUDQ2Wiq64zNwCrlgQGGnXlfPLmRIwrv6/YAujX9ZvNn7+LrQI - 18vM2Dmu2vnqu9riZNveaYuEck39nai1ovArtdtiXIpAzjWZy9S/RSVzaqFq+kYwP0Cf+BQ6V+He - 9jfXVB+z7QYAAA== - headers: - content-encoding: [gzip] - content-length: ['751'] - content-type: [application/json] - status: {code: 200, message: OK} -- request: - body: '{"amount": 1000, "description": "janet"}' - headers: {} - method: POST - uri: https://api.balancedpayments.com:443/cards/CC2SGPDNPBU0oOKbaes9tqC1/debits - response: - body: {string: !!python/unicode "{\n \"debits\": [\n {\n \"status\": - \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"customer\": - \"CU2RCigJCqQDdYQEcThv3Jxr\",\n \"source\": \"CC2SGPDNPBU0oOKbaes9tqC1\",\n - \ \"order\": null,\n \"dispute\": null\n },\n \"updated_at\": - \"2014-07-01T11:33:19.183741Z\",\n \"created_at\": \"2014-07-01T11:33:18.328686Z\",\n - \ \"transaction_number\": \"W826-691-7336\",\n \"failure_reason\": - null,\n \"currency\": \"USD\",\n \"amount\": 1000,\n \"failure_reason_code\": - null,\n \"meta\": {},\n \"href\": \"/debits/WD3SBmZ5oKPKtu9X2gMD9Y0J\",\n - \ \"appears_on_statement_as\": \"BAL*example.com\",\n \"id\": \"WD3SBmZ5oKPKtu9X2gMD9Y0J\"\n - \ }\n ],\n \"links\": {\n \"debits.customer\": \"/customers/{debits.customer}\",\n - \ \"debits.dispute\": \"/disputes/{debits.dispute}\",\n \"debits.source\": - \"/resources/{debits.source}\",\n \"debits.order\": \"/orders/{debits.order}\",\n - \ \"debits.refunds\": \"/debits/{debits.id}/refunds\",\n \"debits.events\": - \"/debits/{debits.id}/events\"\n }\n}"} - headers: - content-length: ['1002'] - content-type: [application/json] - status: {code: 201, message: CREATED} -version: 1 diff --git a/tests/py/fixtures/TestPaydayChargeOnBalanced.yml b/tests/py/fixtures/TestChargeOnBalanced.yml similarity index 55% rename from tests/py/fixtures/TestPaydayChargeOnBalanced.yml rename to tests/py/fixtures/TestChargeOnBalanced.yml index 1200e15abd..8a7410c67a 100644 --- a/tests/py/fixtures/TestPaydayChargeOnBalanced.yml +++ b/tests/py/fixtures/TestChargeOnBalanced.yml @@ -3,46 +3,46 @@ interactions: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2RCigJCqQDdYQEcThv3Jxr + uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd response: body: string: !!binary | - H4sIAAAAAAAAA41Uy27bMBC85ysEnevIkp028NXtJbcU6aEtCmFNshURiXT4CGIY+vcubUmmSaXs - RYfdmSF3dsTjTZblxGojO6Z0vsl+YiHLjqcvtgR0DKvCtu2HsdZy8eygIwhhWlpFQiDWKdOGCzBc - ikFlEOknNbunYBitwSAir5blerH8tFiWT2W5WVWbqrpd35X398sf+UQhiiUoq2p993HlUajc1Z0U - pgmH4dQdu/1Wfd3yPw/bl8fP9PvjF/LUvK4e3tTlzH0jRThg3ij229GLycEirYRGkwaEqbUBY52R - uZCLDgxpLsd1zEBgMXtx0DKPHHSzHRiocDSgVDEdbIpwcwiAuCdcKavmy2VcdhcPvUCRvcRGWxNJ - Z5pEWmHUwe9Gg+ysxntoXc+lTmtRt6DNOhyTdcDbqMivAtfjWb9cftykXngv0b+dIpwX6Nopz7o4 - Tosd+v2wIo9IQNG6kS09rdKLgkfmtC883DnJnsYOxHMN5GRSQuYaGilRtuMpiQEzw/X/1nd88H7p - GTPYm2FKYAr+b5gYHl3K2Zaw5AyJmJTrvTUsQZ5QEd8oEBqIe7wSGlfISAcfCStSM4ygGfYrvszQ - Jq6g2AiLFKSi57f9H+EcMBEXn1qazNMIwv+rv+n/AoxtLhJTBgAA + H4sIAAAAAAAAA41Uy27bMBC85ysEnWvLipQ29i1oby2CAG3RokUh0CQLEZFIhY+ggqF/79KWZJpU + wlx02J0ZcmdHPFwlSYqN0qKlUqW75DcUkuRw/EKLo5ZClZumeTfVGsYfLXQCAUwJI7EPhDqhSjOO + NBN8VBlFhlnNdARpSiqkAZFeb/Jytfmw2uTf8ptdWezKcp0Xxfvb7a90pmBJX6UU622+uSlKh0LE + vmoF17U/DCP22I/fi88/Sf98//UT7b88FN3dD7mtn8j5zK4W3B8wrSX9a+nZ7GAWVwKjcY24rpRG + 2lgjUy5WLdK4Ph/XUo08i+mTheZp4KCdradI+qMhQiRV3qYw070HhD3BSun1cjkPy/bivhcg0glo + NBUWZKGJheFa9m43GGRvFNxDqWopdUrxqkFKl/6YtEWsCYrsInADnPXH5sdO6oT3HP31HOE0A9eO + eVbZYV7s2B/GFTlEjCSpatGQ4yqdKDhkRobMwZ2S7GjsEX+sED6aFJG5hAZKhO5ZTGLELHDdv/UF + H5xfesEM+k9TySEFbxsmhAeXsrZFLDlBAiZhqjOaRsgzKuBribhC2D5eEY0LZKADj4ThsRkm0AL7 + GV5m1ESuIOkECxSEJKe3/ZVwjpiAC08tieZpAsH/NVwN/wHQiK4AUwYAAA== headers: content-encoding: [gzip] - content-length: ['495'] + content-length: ['499'] content-type: [application/json] status: {code: 200, message: OK} - request: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2RCigJCqQDdYQEcThv3Jxr/cards?limit=10&offset=0 + uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd/cards?limit=10&offset=0 response: body: string: !!binary | - H4sIAAAAAAAAA6VVyW7bMBC95ysIHXpKbIlyvAFB4ThBigRx9gJNUQg0F5uITDok5dgI/O8lKcmS - sqBFy4MAzcKZefNm+LoHQICRIjoYgp/2B4BX/7XilIsnJy4FzjLTRi6ostJg/ABvx3x2Pn6+OSE/ - bk7x/XwVn69VUPhv98uLGBczqpaKC+Mc+7jHwg6M4+4hYv0+jmncxSyEvU6XYtTtMIJZ3CUEQxqx - eGDNKR6wmDESHiLY7QW7i/FqlSiqs9TdK7I03WlEtpjmaa5rJ7Kn8kYrnSylNihNFsjguctNyEpP - 10uukOFSJAspjNNH4S7CghrUBEfRmbX10EjBMu1+SnOHHTebxMgXb1FXIEJsETqBTnEs1wC+h5AT - f+8Y3p1dn0yujx9CeXUxRVQPzPO4VhNGhs6k2jhraeYWgl3CZrOkTowVJdw0QSzrb2A4ReIpEWjh - vc6vL69uz0YTMP42ujsFx6PJxT6YtEatJp7aKEpNheeG6spgqpDwdXznGlXibEls1iRBnh0wjDoH - Ye8gjO6jaBjDIYxb/X4fdgaPtVA5ZNa+Tk4LsJXUa8hZTB2yH4gjFy+CMbhEXIA7n3q9MZYaxhf/ - YT8L6mBJvM0ghFFY98YyE0ZtksLAxX83GRiJhNCpbccQGJXRir857I1+1Pi4ocjNIAxhRUg7DG/K - DLhOVlRxxqmDvRHAkX83OkFeOyiYCGybQF4ecNkDIoGQBniW1Brusi/YNAQMpbrKf64oc6i0/W5p - /wVxFf0DCWC/04l7j/lsbC2UvxxaQWMOgxRpz6J2uals7E/2VJ7a15QvuDmKwi+SMU3NUdnDQNB1 - c60EVVHFGvz3y31Um2i5UII8upUUDQ2Wiq64zNwCrlgQGGnXlfPLmRIwrv6/YAujX9ZvNn7+LrQI - 18vM2Dmu2vnqu9riZNveaYuEck39nai1ovArtdtiXIpAzjWZy9S/RSVzaqFq+kYwP0Cf+BQ6V+He - 9jfXVB+z7QYAAA== + H4sIAAAAAAAAA6VVW2+bMBR+76+weNhTmwBOyEWqprSbVq1NWq1dO3WakIPtxiqxmW2yoCr/fbaB + AL1o08YDEud+vvOdw9MBAF6CJFbeFHw3HwA8ubcRp4w/WnEtsJa50mJNpJF6p1/h+TdcbBbXH0hx + cQWz2Z2crH5ir/LfHdaBKOMPRGaScW0dx8mI+oMQwmiI6HicQAKjhPrhaBCRBEUDihMKI4yTkAQU + Tow5SSYUUor9IQqjkbcPnGw2sSQqT21cnqfpXsPz9bIsc9t6AvM03mij4kwojdJ4jXSysrVx0ejJ + NmMSaSZ4vBZcW33g7zOsiUZdcCR5MLYOGsFpruxHbW6xY7qItfjlLNoKhLFpQsWhVZyILQhfQsiw + i3sK52cn6i7anKELqPKLW3xzu7yctBBBmjwIWVhroVcGgn3BusiIFSeSYKa7INb9dzBcIv4Yc7R2 + Xp+v5pdfPs0W4PRsdv0RnMwW54dg0Zv1ungqLQnRDZ4FUY3BUiLu+rhlCjXiPMOmahwjx47QDwZH + /ujID26C4XQAp4NhbzwMQhjct1KVkBn7NjkNwEbS7qFkMbHIviIObD4TGcwR4+Dald4ejKGGds2/ + Os+KOonAzmbih4Hf9k5EzrUs4srA5n+xGQniMSZLM44p0DInDX9L2DvzaPGxIMjuYOiHDSHNMjxr + 02Mq3hDJKCMW9k4CS/796nhl76BiIjBjAmV7wFYPsABcaOBY0hq4rb5i0xRQlKqm/pUk1KLSd7el + /xfEleQPJBhEcBLA+3I3dgbKHxYtr7OHXoqUY1G/vlQm9xt3qiztfcrWTB8H/jtBqSL6uJ6hx8m2 + e1a8pqnqDP57cJfVFFofFK/MbiTVQL1Mkg0TuT3ADQs8Lcy5sn4lUzzK5P83bGB0x/rZxS//Cz3M + VJZrs8fNOJ/cVHsM7/p7bVVQqWn/J1qjqPxq7a5alyqRdY1XInX/opo5rVQtfSeZW6A3fCqd7fBg + 9xv4QHhu7QYAAA== headers: content-encoding: [gzip] content-length: ['751'] @@ -52,17 +52,17 @@ interactions: body: '{"amount": 1061, "description": "janet"}' headers: {} method: POST - uri: https://api.balancedpayments.com:443/cards/CC2SGPDNPBU0oOKbaes9tqC1/debits + uri: https://api.balancedpayments.com:443/cards/CC3MHBsW6vHaL3suLVdTVbO9/debits response: body: {string: !!python/unicode "{\n \"debits\": [\n {\n \"status\": \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"customer\": - \"CU2RCigJCqQDdYQEcThv3Jxr\",\n \"source\": \"CC2SGPDNPBU0oOKbaes9tqC1\",\n + \"CU3KXdyvNSDeyLP3pAWr9hqd\",\n \"source\": \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \ \"order\": null,\n \"dispute\": null\n },\n \"updated_at\": - \"2014-07-01T11:33:03.633978Z\",\n \"created_at\": \"2014-07-01T11:33:03.009437Z\",\n - \ \"transaction_number\": \"W252-963-4934\",\n \"failure_reason\": + \"2014-07-01T15:44:33.253102Z\",\n \"created_at\": \"2014-07-01T15:44:32.580309Z\",\n + \ \"transaction_number\": \"W640-770-4849\",\n \"failure_reason\": null,\n \"currency\": \"USD\",\n \"amount\": 1061,\n \"failure_reason_code\": - null,\n \"meta\": {},\n \"href\": \"/debits/WD3BnKoWXRGDPxtvoKwfPl3P\",\n - \ \"appears_on_statement_as\": \"BAL*example.com\",\n \"id\": \"WD3BnKoWXRGDPxtvoKwfPl3P\"\n + null,\n \"meta\": {},\n \"href\": \"/debits/WD4DH9NCjErBiN50xl2g0AkN\",\n + \ \"appears_on_statement_as\": \"BAL*example.com\",\n \"id\": \"WD4DH9NCjErBiN50xl2g0AkN\"\n \ }\n ],\n \"links\": {\n \"debits.customer\": \"/customers/{debits.customer}\",\n \ \"debits.dispute\": \"/disputes/{debits.dispute}\",\n \"debits.source\": \"/resources/{debits.source}\",\n \"debits.order\": \"/orders/{debits.order}\",\n @@ -80,9 +80,9 @@ interactions: response: body: {string: !!python/unicode "{\n \"customers\": [\n {\n \"name\": null,\n \"links\": {\n \"source\": null,\n \"destination\": - null\n },\n \"updated_at\": \"2014-07-01T11:33:04.159328Z\",\n \"created_at\": - \"2014-07-01T11:33:04.040810Z\",\n \"dob_month\": null,\n \"id\": - \"CU3CxhsF9DKgEuHqdgc1pfjD\",\n \"phone\": null,\n \"href\": \"/customers/CU3CxhsF9DKgEuHqdgc1pfjD\",\n + null\n },\n \"updated_at\": \"2014-07-01T15:44:33.831067Z\",\n \"created_at\": + \"2014-07-01T15:44:33.712647Z\",\n \"dob_month\": null,\n \"id\": + \"CU4EY3YAnRRePOiqhVPwnzJH\",\n \"phone\": null,\n \"href\": \"/customers/CU4EY3YAnRRePOiqhVPwnzJH\",\n \ \"merchant_status\": \"no-match\",\n \"meta\": {\n \"seq\": \"3\"\n },\n \"dob_year\": null,\n \"address\": {\n \"city\": null,\n \"line2\": null,\n \"line1\": null,\n \"state\": @@ -114,16 +114,16 @@ interactions: \ \"customer\": null\n },\n \"fingerprint\": \"9ea2b317b53183f5a93ba23a594a0b8a0f2183ea9cc338e0964755cd9df71b99\",\n \ \"cvv_result\": null,\n \"number\": \"xxxxxxxxxxxx4448\",\n \"avs_postal_match\": null,\n \"expiration_month\": 12,\n \"meta\": {},\n \"id\": - \"CC3DQO5ZMPJ5apmJH2CY5Gfz\",\n \"category\": \"other\",\n \"type\": + \"CC4FZsgPsRQlCDpQ75K0PC17\",\n \"category\": \"other\",\n \"type\": \"credit\",\n \"cvv_match\": null,\n \"bank_name\": \"CREDIT AGRICOLE BANK POLSKA, S.A.\",\n \"avs_street_match\": null,\n \"brand\": - \"Visa\",\n \"updated_at\": \"2014-07-01T11:33:05.211516Z\",\n \"address\": + \"Visa\",\n \"updated_at\": \"2014-07-01T15:44:34.623376Z\",\n \"address\": {\n \"city\": null,\n \"line2\": null,\n \"line1\": null,\n \ \"state\": null,\n \"postal_code\": null,\n \"country_code\": null\n },\n \"can_debit\": true,\n \"name\": null,\n \"expiration_year\": 2020,\n \"cvv\": null,\n \"is_verified\": true,\n \"avs_result\": - null,\n \"can_credit\": false,\n \"href\": \"/cards/CC3DQO5ZMPJ5apmJH2CY5Gfz\",\n - \ \"created_at\": \"2014-07-01T11:33:05.211514Z\"\n }\n ],\n \"links\": + null,\n \"can_credit\": false,\n \"href\": \"/cards/CC4FZsgPsRQlCDpQ75K0PC17\",\n + \ \"created_at\": \"2014-07-01T15:44:34.623373Z\"\n }\n ],\n \"links\": {\n \"cards.card_holds\": \"/cards/{cards.id}/card_holds\",\n \"cards.customer\": \"/customers/{cards.customer}\",\n \"cards.disputes\": \"/cards/{cards.id}/disputes\",\n \ \"cards.debits\": \"/cards/{cards.id}/debits\"\n }\n}"} @@ -135,110 +135,110 @@ interactions: content-type: [application/json] status: {code: 201, message: CREATED} - request: - body: '{"links": {"customer": "/customers/CU3CxhsF9DKgEuHqdgc1pfjD"}, "cvv_result": + body: '{"links": {"customer": "/customers/CU4EY3YAnRRePOiqhVPwnzJH"}, "cvv_result": null, "number": "xxxxxxxxxxxx4448", "expiration_month": 12, "meta": {}, "id": - "CC3DQO5ZMPJ5apmJH2CY5Gfz", "category": "other", "is_verified": true, "type": + "CC4FZsgPsRQlCDpQ75K0PC17", "category": "other", "is_verified": true, "type": "credit", "cvv_match": null, "bank_name": "CREDIT AGRICOLE BANK POLSKA, S.A.", - "avs_street_match": null, "brand": "Visa", "updated_at": "2014-07-01T11:33:05.211516Z", + "avs_street_match": null, "brand": "Visa", "updated_at": "2014-07-01T15:44:34.623376Z", "fingerprint": "9ea2b317b53183f5a93ba23a594a0b8a0f2183ea9cc338e0964755cd9df71b99", "can_debit": true, "name": null, "expiration_year": 2020, "can_credit": false, - "avs_postal_match": null, "avs_result": null, "cvv": null, "created_at": "2014-07-01T11:33:05.211514Z", + "avs_postal_match": null, "avs_result": null, "cvv": null, "created_at": "2014-07-01T15:44:34.623373Z", "address": {"city": null, "line2": null, "line1": null, "state": null, "postal_code": null, "country_code": null}}' headers: {} method: PUT - uri: https://api.balancedpayments.com:443/cards/CC3DQO5ZMPJ5apmJH2CY5Gfz + uri: https://api.balancedpayments.com:443/cards/CC4FZsgPsRQlCDpQ75K0PC17 response: body: string: !!binary | - H4sIAAAAAAAAA41U23KbMBB9z1cwPCc24lLAby52c3Eap0nambrTYQQSthoQVBIeux7+vRIXGzvO - tDwww1l295zdI+0uNE2PIUNcH2k/5Iem7eq3hFNCXxXcAerPkos8w0yievDVCjYr/smfzJbT8uY3 - WsagSH5N9Da/uuwKJYQuMSsYoUIl+hiakQXcyLGAZyUO9K0ImhZ0fBsakQeNxJQ4hn4cW5aHDf+D - 7TpOjHyUuCDyfX1fOF6vQ4Z5maq6tEzTfYSWWdTQ3PQe27a9QzZc87DIuYBpmEERr05r4E1BGBQk - p2GWU6HiwNx3yLCAajgHmQTVYwmsyZe5s/j8eOfAIru7MYPvznXyp8caCrzM2Vb9nYuVpLkvKrYF - VnDMMCLiWOhZjhGkryGFWZ0VPE0nty/a+PrpNpjfT7WP44eZ9ji/f56NL7XnwXhwrJ0LhrE4rz1i - kNZyvhEOD2llgSR5FMJ6kaYB7CvDvTLACwAjyxoZzsAFtuuBRa8TQnJFpz4iQsnvr6wxHDbPw+At - LBcnlOyTIu1G4xydCcZ5SQXbhr3oG7fGkIYIR3L8I02wEh881Yz5yGc9j2wxVOfCNEyjb9AThjrh - 4RozkhCs5nvUQBnyvJ0Vp9YTIy2BKT+wWjGcyEL6sD7Fw/+wH8P/2KEJgAPsRXOQKzmgn0rQyX3Q - 3BoD1TVc5Wl9gXQkdjWXAUFVzaqNN1Pp8no3ybC7VviwzeyAqvVRm4QIL0qB32m1jx41qlf5XkYT - k+Kqi+ovvj4JHQsFAAA= + H4sIAAAAAAAAA41UXW+bMBR9769APLcJYCghbxnNti7VkqZdpWaakMEmsQqG2SZtFvHfZ/OREJpO + 4wGJc7n3nnPvsfcXmqZHkCGuj7Wf8kPT9tVbwgmhLwpuAfVnwUWWYiZR3f9hT5/B84Qul3gxJ783 + T4tX+ufbV73JLy/bQjGha8xyRqhQiR6GVghMN3SAOQKxAz0QQgtAx7OhEY6gEVsSx9CLIgBG2PCu + bddxIuSh2DVDz9MPhaPtNmCYF4mqS4skOURokYY1zbfOY9v26JgNtzzIMy5gEqRQRJt+DfyWEwYF + yWiQZlSouGkdOqRYQDWco0yCqrH49ucVXy/48j7xb/J715kZC990O6yhwOuM7dTfmdhImoeiYpdj + BUcMIyJOhZ7lGEL6ElCYVln+cnpz+6hNvixv/fndVPs0+T7TFvO7h9nkUnsYTAan2rlgGIvz2kMG + aSXniXB4TCtyJMmjAFaLtAzTvjLcK8N8NJ2xbY+BM7i2TMNxV51OCMkV9X1EhJLfXVltOGydh833 + sFycULJ7RZqNRhk6E4yyggq2CzrRd26NIA0QDuX4x5pgBT56qh7zic86HtlhqM6FZVhG16A9hjrh + wRYzEhOs5nvSQBnyvJ0Vp8YTYy2GCT+y2jAcy0L6sDrFw/+wH8P/3KEtdwiAC1b1QS7lgH4pQb37 + oL41BqprsMmS6gJpSewrLgOCyopVE6+n0uZ1bpJhe63wYZPZAmXjoyYJEZ4XAn/Q6hA9aVSt8qOM + OibFlRflX+h1jAQLBQAA headers: content-encoding: [gzip] - content-length: ['584'] + content-length: ['585'] content-type: [application/json] status: {code: 200, message: OK} - request: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU3CxhsF9DKgEuHqdgc1pfjD + uri: https://api.balancedpayments.com:443/customers/CU4EY3YAnRRePOiqhVPwnzJH response: body: string: !!binary | - H4sIAAAAAAAAA41Uy27bMBC85ysEnetIslw08TVpUSDX9NKiENYkEzGRSIWPIoahf+/SlmRaVMtc - dNidGXJnRzxcJUlKrDayZUqn2+QXFpLkcPxiS0DLsCps03waaw0Xrw46ghCmpVVkDsQ6ZdpwAYZL - MagMIv2kZjsKhtEKDCLSdV5sVvmXVV48FsW2LLf55rr4fFuub36mE4UoFqHkm/ymyD0KlbuqlcLU - 82E4dcfe/Sjv3mv97fb+4fmr/f5Gn0nRPb3cn8/sainmA6a1Yk+Onk0OZnElNJrUIEylDRjrjEyF - XLVgSH0+rmUGZhazNwct08BBN9uegZqPBpQqpmebItzsZ0DcE66UrZfLRVh2F597gSKdxEZTEUkX - mkRaYdTe7waD7KzGe2hdLaVOa1E1oM1mPiZrgTdBkV8Ersezfrv8uEm98J6jfz1FOM3QtWOedXaY - Fjv0+2FFHpGAolUtG3pcpRcFj8xpn3m4U5I9jR2I1wrI0aSIzCU0UKJsx2MSA2aB6/+t//DB+6UX - zGDvhimBKfjYMCE8uJSzLWLJCRIwKdedNSxCnlAB3ygQGoh7vCIaF8hABx8JK2IzjKAF9h98maGJ - XEGxERYoSEVPb/t/wjlgAi4+tTSapxGE/1d/1f8Fs5efT1MGAAA= + H4sIAAAAAAAAA41Uy27bMBC85ysEnevIstS48C0oAgS9NAjaAmlRCGuShYhIpMNHW9fQv3dpSzIt + qmEvOuzODLmzIx6ukiQlVhvZMqXTTfINC0lyOH6xJaBlWBW2ad4MtYaLZwcdQAjT0ioyBWKdMm24 + AMOl6FV6kW5UszsKhtEKDCLS1TIvF8v1Ypl/yt9uynJTFNfvinx5s/6ajhSiWISyzlc3pU+hclu1 + Uph6Ogyn7tj3n8u7p+LpVjw+soeP/KX+8vBL/Plwfz5zV0sxHTCtFfvh6NnoYBZXQqNJDcJU2oCx + zshUyEULhtTn41pmYGIxe3HQIg0cdLPtGajpaECpYnqyKcLNfgLEPeFK2Wq+nIdld/GpFyiyk9ho + KiLpTJNIK4za+91gkK3VeA+tq7nUaS2qBrQpp2OyFngTFPlF4Do867vLj5vUC+85+tdjhNMMXTvm + WWeHcbF9v+tX5BEJKFrVsqHHVXpR8MicdpmHOyXZ09iCeK6AHE2KyFxCAyXKtjwm0WNmuP7f+g8f + vF96xgz22zAlMAX/N0wIDy7lbItYcoIETMr1zhoWIY+ogG8UCA3EPV4RjQtkoIOPhBWxGQbQDPsn + vszQRK6g2AALFKSip7f9lXD2mICLTy2N5mkA4f/VXXV/AWr/a3hTBgAA headers: content-encoding: [gzip] - content-length: ['494'] + content-length: ['498'] content-type: [application/json] status: {code: 200, message: OK} - request: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU3CxhsF9DKgEuHqdgc1pfjD/cards?limit=10&offset=0 + uri: https://api.balancedpayments.com:443/customers/CU4EY3YAnRRePOiqhVPwnzJH/cards?limit=10&offset=0 response: body: string: !!binary | - H4sIAAAAAAAAA6VU227bMAx971cYethTm1i+LHGAYsiSrtetXdsNWIfBkC050WrLriQHyYr8+yTZ - jp00xYYtDwZCiuQhzyGfDywLxIhjAUbWd/XHsp7NV5lTyh61uTHol6WQeUa4soLJF3eynIsPwfRy - dlKePeFZDIvk5xTU8evDJlFC2YzwglMmdWBAkBO5cBD5Lhy6iY8CN0KOi/zAQ3Y0RHbiKDtBQRy7 - 7pDYwVtv4PsxDnAygFEQgE3ieLEIORFlqvOyMk03HlZmUQVz2fl5njdso9FChEUuJErDDMl4vpuD - LAvKkaQ5C7OcSe2HzqZCRiTSw2nbpNiMZeJOP1/7Dx9vLnxUZBdnzuSbf5r86qBGksxyvtKvczlX - MDdJ5aog2hxzgqncbnQvxgixx5ChzERNbk+m5/fW+PT2fHJ9dWK9H3+6tG6ur+4ux4fWXW/c2+5d - SE6I3N97xBEz7XylArVhZYEVeBwiQ6RjQ+/IHhzZ8B7CkeuObL83gN5gCB86lTBWFO3qiErdfpey - SnDE2W+GL82KOKnb3klSMxrneI8zzksm+SrseF+oNUYsxCRS4x9Zkpek1VQ15i2ddTSyIkjvhWM7 - dlegOwgBFeGCcJpQoue7VUALcr+cNaZaEyMrQaloUc05SVQi0Ddb3P8L+XHyBw4dCH3oPVSLvFYD - +qEbAo3izcRAioQRQb+5Car2KxehgvYupRmVx9B+kyeJIPLYrkUCGFluLzBom6oPzr8nN1UVUFiz - AqrqytIYCk4WNC+1RFtugczVYdBxFZkgofz/G1ajM/di57ZWF7iHqShKSTSQhs5nw2qP4nV/460B - VZ7uRe5QUcc13nUz6jpIJQ3neWqu/p5S+lXt3ypm1uKVmNqnOzxY/wZttdUKVwYAAA== + H4sIAAAAAAAAA6VU30/bMBB+56+I/LAnaPNzaSqhqStsY6BRCkOCaYqc5EItEieznY4O9X+f7SRN + Wso0bX2I1Dvf3Xf3fXfPB4aBYswSjsbGN/nHMJ71V5ozQh+VuTWolxUXRQ5MWtH0q3t659xN6HwO + s0vyY3E7+0l/ff6Emvj1YZsoJfQBWMkIFSowAGxHjuVHnmONnNTDgRNh28Fe4GIzGmEztaUdcBDH + jjMCM3jr+p4XJ0GS+lYUBGiTOF4uQwa8ylReWmXZxkOrPKphPvV+ruuOumi85GFZcIGzMMciXuzm + gKeSMCxIQcO8oEL5LXtTIQeB1XC6NkmixzJ1P9zzhxmfX2XTk/LK987N2dTye6ixgIeCrdTrQiwk + zE1SsSpBmWMGCRHbje7FGGH6GFKc66jp/PTk7MaYfJyfTS8vTo33ky/nxuzy4vp8cmhcDyaD7d65 + YABif+8Rw1S3c0s47sKqMpHgkxBrIm3Tco9M/8i0bixv7Lpjxxu8tS3T8+97lZJEUrSrIyJU+33K + asGBvd9svTRL4oRqeydJw2hcJHuccVFRwVZhz/tCrTGmYQKRHP/YEKyCTlP1mLd01tPICrDaC9u0 + zb5AdxAiwsMlMJISUPPdKqAEuV/OClOjibGR4ox3qBYMUpkIDfUWD/9Cfgz+yKErOXQc37mvF3kt + B/RdNYRaxeuJoQxzLYJhexNk7VcuQg3tXUZyIo4t802RphzEsdmIBFF42l5g1DXVHJx/T66rSqBW + wwqqq0tLaygZLElRKYl23CJRyMOg4moyUUrY/zcsR6fvxc5trS/wICG8rAQoIC2dz5rVAUnWw423 + AVR7+he5R0UT13rX7aibIJk0XBSZvvp7SqlXjX+rmF6LV2Ian+rwYP0b8YzZnFcGAAA= headers: content-encoding: [gzip] - content-length: ['676'] + content-length: ['677'] content-type: [application/json] status: {code: 200, message: OK} - request: body: '{"amount": 1061, "description": "whatever username"}' headers: {} method: POST - uri: https://api.balancedpayments.com:443/cards/CC3DQO5ZMPJ5apmJH2CY5Gfz/debits + uri: https://api.balancedpayments.com:443/cards/CC4FZsgPsRQlCDpQ75K0PC17/debits response: body: {string: !!python/unicode "{\n \"errors\": [\n {\n \"status\": \"Payment Required\",\n \"category_code\": \"card-declined\",\n \"additional\": \"Account Frozen\",\n \"status_code\": 402,\n \"category_type\": - \"banking\",\n \"extras\": {},\n \"request_id\": \"OHM78c3ee8a011311e4a4e806429171ffad\",\n - \ \"description\": \"R758: Account Frozen. Your request id is OHM78c3ee8a011311e4a4e806429171ffad.\"\n + \"banking\",\n \"extras\": {},\n \"request_id\": \"OHM9b39af72013611e4b7da06429171ffad\",\n + \ \"description\": \"R758: Account Frozen. Your request id is OHM9b39af72013611e4b7da06429171ffad.\"\n \ }\n ],\n \"links\": {\n \"debits.customer\": \"/customers/{debits.customer}\",\n \ \"debits.dispute\": \"/disputes/{debits.dispute}\",\n \"debits.source\": \"/resources/{debits.source}\",\n \"debits.order\": \"/orders/{debits.order}\",\n \ \"debits.refunds\": \"/debits/{debits.id}/refunds\",\n \"debits.events\": \"/debits/{debits.id}/events\"\n },\n \"debits\": [\n {\n \"status\": \"failed\",\n \"description\": \"whatever username\",\n \"links\": - {\n \"customer\": \"CU3CxhsF9DKgEuHqdgc1pfjD\",\n \"source\": - \"CC3DQO5ZMPJ5apmJH2CY5Gfz\",\n \"order\": null,\n \"dispute\": - null\n },\n \"updated_at\": \"2014-07-01T11:33:07.337495Z\",\n \"created_at\": - \"2014-07-01T11:33:07.058857Z\",\n \"transaction_number\": \"W137-025-4334\",\n + {\n \"customer\": \"CU4EY3YAnRRePOiqhVPwnzJH\",\n \"source\": + \"CC4FZsgPsRQlCDpQ75K0PC17\",\n \"order\": null,\n \"dispute\": + null\n },\n \"updated_at\": \"2014-07-01T15:44:37.542581Z\",\n \"created_at\": + \"2014-07-01T15:44:37.270803Z\",\n \"transaction_number\": \"W081-671-9798\",\n \ \"failure_reason\": \"R758: Account Frozen.\",\n \"currency\": \"USD\",\n \"amount\": 1061,\n \"failure_reason_code\": \"card-declined\",\n - \ \"meta\": {},\n \"href\": \"/debits/WD3FVZLdNCGAUQ0VtJ2htxRn\",\n - \ \"appears_on_statement_as\": \"BAL*example.com\",\n \"id\": \"WD3FVZLdNCGAUQ0VtJ2htxRn\"\n + \ \"meta\": {},\n \"href\": \"/debits/WD4IXVOElpbeHACesZUPkr09\",\n + \ \"appears_on_statement_as\": \"BAL*example.com\",\n \"id\": \"WD4IXVOElpbeHACesZUPkr09\"\n \ }\n ]\n}"} headers: content-length: ['1426'] @@ -252,9 +252,9 @@ interactions: response: body: {string: !!python/unicode "{\n \"customers\": [\n {\n \"name\": null,\n \"links\": {\n \"source\": null,\n \"destination\": - null\n },\n \"updated_at\": \"2014-07-01T11:33:07.979850Z\",\n \"created_at\": - \"2014-07-01T11:33:07.864901Z\",\n \"dob_month\": null,\n \"id\": - \"CU3GPWAhne1hO9gpoRn8fOS1\",\n \"phone\": null,\n \"href\": \"/customers/CU3GPWAhne1hO9gpoRn8fOS1\",\n + null\n },\n \"updated_at\": \"2014-07-01T15:44:38.120756Z\",\n \"created_at\": + \"2014-07-01T15:44:38.013455Z\",\n \"dob_month\": null,\n \"id\": + \"CU4JOi5FNtTIATtNhChk2T8Z\",\n \"phone\": null,\n \"href\": \"/customers/CU4JOi5FNtTIATtNhChk2T8Z\",\n \ \"merchant_status\": \"no-match\",\n \"meta\": {\n \"seq\": \"4\"\n },\n \"dob_year\": null,\n \"address\": {\n \"city\": null,\n \"line2\": null,\n \"line1\": null,\n \"state\": @@ -286,16 +286,16 @@ interactions: \ \"customer\": null\n },\n \"fingerprint\": \"1e2c425a579a1c7f27a037f0662b855035ceadbff18a0adbe785fb07e2afe1cc\",\n \ \"cvv_result\": null,\n \"number\": \"xxxxxxxxxxxx4242\",\n \"avs_postal_match\": null,\n \"expiration_month\": 12,\n \"meta\": {},\n \"id\": - \"CC3HTH0bkqdVstmShNmjWL8V\",\n \"category\": \"other\",\n \"type\": + \"CC4L2Pd5dVpoGgLm6cVV32AJ\",\n \"category\": \"other\",\n \"type\": \"credit\",\n \"cvv_match\": null,\n \"bank_name\": \"\",\n \"avs_street_match\": - null,\n \"brand\": \"Visa\",\n \"updated_at\": \"2014-07-01T11:33:08.809298Z\",\n + null,\n \"brand\": \"Visa\",\n \"updated_at\": \"2014-07-01T15:44:39.117159Z\",\n \ \"address\": {\n \"city\": null,\n \"line2\": null,\n \ \"line1\": null,\n \"state\": null,\n \"postal_code\": null,\n \"country_code\": null\n },\n \"can_debit\": true,\n \ \"name\": null,\n \"expiration_year\": 2020,\n \"cvv\": null,\n \ \"is_verified\": true,\n \"avs_result\": null,\n \"can_credit\": - false,\n \"href\": \"/cards/CC3HTH0bkqdVstmShNmjWL8V\",\n \"created_at\": - \"2014-07-01T11:33:08.809295Z\"\n }\n ],\n \"links\": {\n \"cards.card_holds\": + false,\n \"href\": \"/cards/CC4L2Pd5dVpoGgLm6cVV32AJ\",\n \"created_at\": + \"2014-07-01T15:44:39.117156Z\"\n }\n ],\n \"links\": {\n \"cards.card_holds\": \"/cards/{cards.id}/card_holds\",\n \"cards.customer\": \"/customers/{cards.customer}\",\n \ \"cards.disputes\": \"/cards/{cards.id}/disputes\",\n \"cards.debits\": \"/cards/{cards.id}/debits\"\n }\n}"} @@ -307,34 +307,34 @@ interactions: content-type: [application/json] status: {code: 201, message: CREATED} - request: - body: '{"links": {"customer": "/customers/CU3GPWAhne1hO9gpoRn8fOS1"}, "cvv_result": + body: '{"links": {"customer": "/customers/CU4JOi5FNtTIATtNhChk2T8Z"}, "cvv_result": null, "number": "xxxxxxxxxxxx4242", "expiration_month": 12, "meta": {}, "id": - "CC3HTH0bkqdVstmShNmjWL8V", "category": "other", "is_verified": true, "type": + "CC4L2Pd5dVpoGgLm6cVV32AJ", "category": "other", "is_verified": true, "type": "credit", "cvv_match": null, "bank_name": "", "avs_street_match": null, "brand": - "Visa", "updated_at": "2014-07-01T11:33:08.809298Z", "fingerprint": "1e2c425a579a1c7f27a037f0662b855035ceadbff18a0adbe785fb07e2afe1cc", + "Visa", "updated_at": "2014-07-01T15:44:39.117159Z", "fingerprint": "1e2c425a579a1c7f27a037f0662b855035ceadbff18a0adbe785fb07e2afe1cc", "can_debit": true, "name": null, "expiration_year": 2020, "can_credit": false, - "avs_postal_match": null, "avs_result": null, "cvv": null, "created_at": "2014-07-01T11:33:08.809295Z", + "avs_postal_match": null, "avs_result": null, "cvv": null, "created_at": "2014-07-01T15:44:39.117156Z", "address": {"city": null, "line2": null, "line1": null, "state": null, "postal_code": null, "country_code": null}}' headers: {} method: PUT - uri: https://api.balancedpayments.com:443/cards/CC3HTH0bkqdVstmShNmjWL8V + uri: https://api.balancedpayments.com:443/cards/CC4L2Pd5dVpoGgLm6cVV32AJ response: body: string: !!binary | - H4sIAAAAAAAAA41U32+bMBB+71+BeF4T24RC8jb1YX2Y1mntWqnVhIx9BC9gmG2iRhH/+2x+JCRN - p/GA4M7f3Xd3n29/5Xk+o4prf+W92h/P23dvay6E3DjzaHAnG22qEpS1+rc/gy/fnz/nEnB+v1zX - 1Q8ZZ/cP2B/w7acxUCbkGlSthDQOiIGwBQlpGC0pZlFGIoqCKEM3NySNwxAFIQPK0yzDMUX2A6I4 - zFIUAaEZYMb8Q2C23SYKdFO4uLIpioNHNmXa03ybPAuyIEc03eqkrrShRVJSw/LzGPBWC0WNqGRS - VtI4PyaHDCUY6ppzLFPwri23wd3jHUo3f/iTNuVD/q38/fw1fpqwpgbWldq505XJLc1DULOrwZmZ - Ai7MaaEXOaZUbhJJyw51Wpo2CsBcLi1VVHZsn4SmR1hTc8uNJ7SbE0F4cY2ia4QfMV4FwQotZwTH - eBG/TDJxbidwLhNhXHXTifR6AnLZjN+b7VyMq+osyDAwVvELTlY10qhdMvG+EyOjMuGQ2u6uPKMa - OEqm7+KJjCYS2AF1sieIoKn+zhj6QidbUCIT4Pp7ksDp7bJaHadh5Csvo4U+ssoVZDaQP+8u6fw/ - 1KXgnzOMZzFakmX40t/T1jbolyvo7Lr3S2HmsiZ5VXT7YSSx77jMBG87VoO/78qImyyK+bg19HxA - joZ20NEA4kLXjYEPUh28J4m6UX6E6H22uPaq/Qv9vDF+6gQAAA== + H4sIAAAAAAAAA41UTY+bMBC9769AnLsJdiAkua0itepqte0hzWGrChl7CFbAINtEG0X899p8J5tV + yyEiM7yZN/OefXlwHJcSyZS7cX6bP45zaX5NOOPiaMN9wH5ZKV3kIE3U3f7yn3/w4Our3n1/2unX + dJse8W715nb4+ktfKOHiALKUXGgLRICpjwMShGuCaJjgkHiLMPGWSxyvgsBbBBQIi5MErYhnXiBc + BUnshYBJAohSdyhMT6dIgqoyW1dUWTZkRJXHLc33yeNjH49oclJRWShNsignmqa3NeC95JJoXogo + L4S2eYSHDjloYpczjslZs5at/4J/soDty+Lb4SVf0v1+gZ+eJ6yJhkMhz/brQqeG5lBUn0uwYSqB + cX096F2OMRHHSJC8QV2PprQE0PdHiyURDds9V2SEVSUz3FhEGp2wh/xHL3z00A4FG9/fLNazcL3A + yGg8MCaMGQVubcK1nW6qSOsnwPfD6GPY6KLtVDdFOsFowe4kaVEJLc/RJPvBjJSIiEFstrtxtKxg + tEy7xSsbTSxwBmJtjz3sTf13w9DlKjqB5AkHu9+rBtZv991qOXWSb5yEZGpklUpITCF33hzS+X+4 + S8I/NEQoRMGyO6e1WdAfO9DNcW8vhZntGqVF1twPPYlLw2XGWd2w6vLtVnrc5KKY97eGmnfIPlB3 + PupAjKuy0vBJqyF71aiR8jNEmzPD1Q/1X+Fd3UjqBAAA headers: content-encoding: [gzip] - content-length: ['550'] + content-length: ['546'] content-type: [application/json] status: {code: 200, message: OK} - request: @@ -348,16 +348,16 @@ interactions: \ \"customer\": null\n },\n \"fingerprint\": \"d34d613121ae61ae732d86eaf09929617bd3f5ccc60f7364d32ae1afc5f81745\",\n \ \"cvv_result\": null,\n \"number\": \"xxxxxxxxxxxx4242\",\n \"avs_postal_match\": null,\n \"expiration_month\": 12,\n \"meta\": {},\n \"id\": - \"CC3J5cFlr9RfnD3ztsdDa3xf\",\n \"category\": \"other\",\n \"type\": + \"CC4MOdbG69Yqk2AzU5zkwHdD\",\n \"category\": \"other\",\n \"type\": \"credit\",\n \"cvv_match\": null,\n \"bank_name\": \"\",\n \"avs_street_match\": - null,\n \"brand\": \"Visa\",\n \"updated_at\": \"2014-07-01T11:33:09.854875Z\",\n + null,\n \"brand\": \"Visa\",\n \"updated_at\": \"2014-07-01T15:44:40.681317Z\",\n \ \"address\": {\n \"city\": null,\n \"line2\": null,\n \ \"line1\": null,\n \"state\": null,\n \"postal_code\": null,\n \"country_code\": null\n },\n \"can_debit\": true,\n \ \"name\": null,\n \"expiration_year\": 2030,\n \"cvv\": null,\n \ \"is_verified\": true,\n \"avs_result\": null,\n \"can_credit\": - false,\n \"href\": \"/cards/CC3J5cFlr9RfnD3ztsdDa3xf\",\n \"created_at\": - \"2014-07-01T11:33:09.854872Z\"\n }\n ],\n \"links\": {\n \"cards.card_holds\": + false,\n \"href\": \"/cards/CC4MOdbG69Yqk2AzU5zkwHdD\",\n \"created_at\": + \"2014-07-01T15:44:40.681315Z\"\n }\n ],\n \"links\": {\n \"cards.card_holds\": \"/cards/{cards.id}/card_holds\",\n \"cards.customer\": \"/customers/{cards.customer}\",\n \ \"cards.disputes\": \"/cards/{cards.id}/disputes\",\n \"cards.debits\": \"/cards/{cards.id}/debits\"\n }\n}"} @@ -369,83 +369,83 @@ interactions: content-type: [application/json] status: {code: 201, message: CREATED} - request: - body: '{"links": {"customer": "/customers/CU3GPWAhne1hO9gpoRn8fOS1"}, "cvv_result": + body: '{"links": {"customer": "/customers/CU4JOi5FNtTIATtNhChk2T8Z"}, "cvv_result": null, "number": "xxxxxxxxxxxx4242", "expiration_month": 12, "meta": {}, "id": - "CC3J5cFlr9RfnD3ztsdDa3xf", "category": "other", "is_verified": true, "type": + "CC4MOdbG69Yqk2AzU5zkwHdD", "category": "other", "is_verified": true, "type": "credit", "cvv_match": null, "bank_name": "", "avs_street_match": null, "brand": - "Visa", "updated_at": "2014-07-01T11:33:09.854875Z", "fingerprint": "d34d613121ae61ae732d86eaf09929617bd3f5ccc60f7364d32ae1afc5f81745", + "Visa", "updated_at": "2014-07-01T15:44:40.681317Z", "fingerprint": "d34d613121ae61ae732d86eaf09929617bd3f5ccc60f7364d32ae1afc5f81745", "can_debit": true, "name": null, "expiration_year": 2030, "can_credit": false, - "avs_postal_match": null, "avs_result": null, "cvv": null, "created_at": "2014-07-01T11:33:09.854872Z", + "avs_postal_match": null, "avs_result": null, "cvv": null, "created_at": "2014-07-01T15:44:40.681315Z", "address": {"city": null, "line2": null, "line1": null, "state": null, "postal_code": null, "country_code": null}}' headers: {} method: PUT - uri: https://api.balancedpayments.com:443/cards/CC3J5cFlr9RfnD3ztsdDa3xf + uri: https://api.balancedpayments.com:443/cards/CC4MOdbG69Yqk2AzU5zkwHdD response: body: string: !!binary | - H4sIAAAAAAAAA41U227bMAx971cYfl4TS3J8ydvQYgP20qG7FOgwGIpExUJt2ZDlIFnhf5/kS+Kk - 6TADNmBShzwkj/h643k+o5o3/tr7ZX8877X/WnMh1YszTwZ3sm1MVYK2Vv/uB/n89eljrgDlD+m2 - rh5VIh6+IX/Edx+mQEKqLehaS2UckJOQR4ggjChE9o0J5kkEVARpitMIxRtOxIoxFgUiJlHICaaA - qGArkaA4XPnHwGy3yzQ0beHiqrYojh7VlpuB5n72hDjEJzTdNVldNYYWWUkNyy9jwL6WmhpZqays - lHF+hI8ZSjDUNedUpuR9W+7IlxX7VOj0Uah78sc0/J6SvZixpga2lT6405XJLc1jUHOowZmZBi7N - eaFXOW6oeskULXvUeWmN0QDmemkbTVXP9qds6AnW1txy4xnt54QDFN4G8W2AviO0JmSNggWOEozC - 51kmzu0ELmUijatuPpFBT4Cvm9Fbs52LcVVdBBkHxip+xcmqVhl9yGbeN2JkVGUcNra7a8/oFk6S - Gbp4JqOZBA5AnexxQIK5/i4Y+rLJdqClkOD6e5bA6e26Wh2nceRrT9CiObHKNQgbyF/2l3T5H+rS - 8K8ZBukiWYVJjJ+He9rZBv12BV1c92EpLFzWLK+Kfj9MJF57LgvJu57V6B+6MuFmi2I5bY1mOSIn - QzfqaARx2dStgXdSHb1nifpRvocYfLa47qb7C+9XDG3qBAAA + H4sIAAAAAAAAA41U226jMBB971cgntsEG0Mub1VWe5O2fUlX2q5WyNimWAHD2ibbNOLfaxtISJpW + GylIzHBmzsw59v7K83yCJVX+0vttXjxv754mXHCxseEhYL9slK5KJk3UXz2g7/c8+nyn199u1/ou + X+UbuJ4/+j2+vR4KZVw8MVlLLrQF0hDRGIQAAsxi85+FkM5jhrNgsYCLGMxSGmYRISQOslkYIxpC + zADOSJTNwQxF/qEw2W4TyVRT2LqiKYpDRjRl2tF8Hv0QRPCIxluV1JXSuEhKrEl+XoM911xizSuR + lJXQNg/goUPJNLbLOY7JqVvLCv24p+mXePHr7wbevjxEL5t/X+mnEWus2VMld/brSueG5qGo3tXM + holklOvTQS9yTLHYJAKXDnU6mtKSMX15tFRi4dj+5AofYU1NDTeaYKcTDAC6CWY3AViDaInQEoGJ + EW2OosdRJ0qNAuc24dpON1ak8xODl8Pgbdjoou1UZ0V6wUhFLyRJ1Qgtd8ko+8aMBIuEstRsd+lp + 2bCjZbotnthoZIEdw9b2MAiDsf/OGPpcJVsmecaZ3e9JA+u3y261nHrJl16GC3VklUuWmUL+1B3S + 6X+4S7IPNQwm8dwcPqOhW01rnn/sQGfHvbsUJrZrkleFux8GEnvHZcJp61j1+W4rA250UUyHW0NN + e+QQaHsf9SDKVd1o9k6rQ/akkZPyPUSXM8O1V+0rTJM7KOoEAAA= headers: content-encoding: [gzip] - content-length: ['549'] + content-length: ['551'] content-type: [application/json] status: {code: 200, message: OK} - request: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU3GPWAhne1hO9gpoRn8fOS1 + uri: https://api.balancedpayments.com:443/customers/CU4JOi5FNtTIATtNhChk2T8Z response: body: string: !!binary | - H4sIAAAAAAAAA41Uy27bMBC85ysEnetIit36cSt66DFBHyjQohBochMRkUiFpIoahv69S1uSaVIN - c9Fhd2bInR3xeJMkKe20kQ0one6SX1hIkuPpiy1BGsCq6Or63ViruXi20BGEMC07RX0g1hlowwUx - XIpBZRDpJ7WuZcQAK4lBRHqXF6tFvl7kxbei2C2Xu3x9u11vN+/zn+lEoQoilM2H1TYvHAqT+7KR - wlT+MJzZYz99X35++PGxElBU99unVn4Rm8f7r8XlzLaSwh8wrRQ8Wno2OZjFldBoWhFhSm2I6ayR - qZCLhhhaXY5rwBDPYnix0FUaOGhnOwBR/miEMQXa2xTl5uABcU+4UribLxdh2V7c9wJFWomNuqSS - zTSp7IRRB7cbDLLvNN5D63IudVqLsibarPwxoSG8Dor8KnA9nvXb5sdO6oT3Ev3bKcJphq6d8qyz - 47TYod8PK3KIlChWVrJmp1U6UXDInPWZgzsn2dHYE/FcEnoyKSJzDQ2UGOx5TGLAzHDdv/U/Pji/ - 9IwZ8NeAEpiCtw0TwoNLWdsilpwhAZNx3XYGIuQJFfCNIkITah+viMYVMtDBR6ITsRlG0Az7D77M - pI5cQcEICxSkYue3/ZVwDpiAi08ti+ZpBOH/1d/0/wC0V4mPUwYAAA== + H4sIAAAAAAAAA41UsW7bMBDd8xWC5jqSHKkJvBUBCrRDurhLgkKgSRYiLJEueSpqGPr3Hm1JoUkl + zKLh+N4T793jnW6SJKW9AdVxbdJN8oKFJDmdv3gkScexKvu2/TTVWiH3FjqBEGZUr6kPxDrjBoQk + IJQcVUaRYVbrD4wAZzUBRKTrvChX+f0qL7ZFtSnLzd3DbbHO76vPz+lMoZpHKHlxV1aVQ2FqV3dK + QuM3I5j97ePP8vsPUX19gu23L1t4ah6b/Xr74AgcGiX9BtNG89+Wns0OZnElNJo2REJtgEBvjUyl + WnUEaPPaYseBeBbzPxZapoGDtrcjJ9pvjTCmufEmRQUcPSDOCUfK18vlIizbi/teoMhB4UFbU8UW + DqnqJeijexo0susN3sOYeil1xsi6JQZKv03eEdEGRXEVuAH/9cvmx3bqhPc1+rdzhNMMXTvn2WSn + ebDj+TCOyCFSolndqJadR+lEwSELNmQO7pJkR2NH5L4m9GxSROYaGigxvhMxiRGzwHVf6xs+OE96 + wQz+D7iWmIKPNRPCg0tZ2yKWXCABkwlz6IFHyDMq4IMm0hBql1dE4woZ6OCS6GWshwm0wP6Lm5m0 + kStoPsECBaXZZbe/E84RE3Bx1bJoniYQvq/hZvgPbDJrDlMGAAA= headers: content-encoding: [gzip] - content-length: ['496'] + content-length: ['494'] content-type: [application/json] status: {code: 200, message: OK} - request: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU3GPWAhne1hO9gpoRn8fOS1/cards?limit=10&offset=0 + uri: https://api.balancedpayments.com:443/customers/CU4JOi5FNtTIATtNhChk2T8Z/cards?limit=10&offset=0 response: body: string: !!binary | - H4sIAAAAAAAAA+VWW2vbMBR+768IftjTmkjyNYEyRstWxlhH27XQMYysS6PVlj1ZDslK/vskXxIn - ddexMQpbIME5x+foXL7zHd0fjEYOwYqWzmz02fwZje7rXyNOhbyz4k5g36xKnWdMGalz/Ml9+/H6 - 9VwyOD+b3hb5uYz42QV0Wvv1y84RF/KWqUIJqa0hdT0aQBciiFlgvqGLaBQwzMF0iqYBDBPqcp8Q - EgAeuoFHXYQZxJz4PIKh5zsbx2SxiBUrq9T6lVWabjSyypImzGXv4yEPba3xooyLvNQ4jTOsyXzf - B1sWQmEtchlnudRWD9HmhIxpbIuzTVPQuizH7jufvEnV9JzLE/e7LukJdpe8FzXW7DZXK/t2rucm - zI1TvSqYFRPFqNC7iQ7GmGB5F0uc1Va7qZVaMaaHU0sUlnW0V6LEW7OqoCY2GuO6TwhA7xCEhwBe - Qjhz3RkEYxRECHo3vZMoNR3Yh4nQNrt+Rxo8MTQshg/Fpi/aZrXnpG0YyemAkuSV1GoV97QPwEiw - jClLTHVnI60qtoVMU8UdGPUgsGLYwh4BF/TxtxehI8p4wZTggtn67hxg8TaMVhtT2/LZiOO03EY1 - V4wbR86kHtLJL6BLsZ/1EEzHke9FIbpp5rSF718fesgQ8ZCP/XCKIQk5CjFwQw6CACWR7wPXJwzT - hHMYYWAeWBj5PAEhQ5gzSMjuLAyX8VmH/vTyFCR33+hVqbOL+Yfs6/X76OofGHoDGAQj6EX/9dCj - Zx76p9D1xNBH4wiY3ep3Q29Y8YtlMafbYjVNOikua+afdHveEM4jW77ho1epyIQ+guBFznnJ9BFo - Ee9Ittxdys6WydpLxO87r081gcK2K05zupF0gkKxhcgru5e2hO7o3Cx7S+INgztcqD9P2JSuJtG9 - +1Jzqxr3b0y9st7XdL7RrruyNWIqyqLSzEbfEX9rIOh6stG2WbSujMN4nqf1TW7Ayr7V6nfs6l34 - iE2rsxkerH8AQzNlEisKAAA= + H4sIAAAAAAAAA+VWbW/TMBD+vl9R5QOfWGs7cV4qTWgaYjDBxoeuEiAUOX5ZreYNxynrpv537CRt + 065jCIQmQaVG7V3u/Nzdc3e+PxoMHEoUq5zx4Iv5MxjcN08jTmU+t+K1wL5ZV7rIuDJS5+zau7iS + +M2lnrw7nejL2dlsjibhZ6ezX71cOxIyv+GqVDLX1hByRD2ECQ4iAmkgUECAGwjg+ygJMQYuppyw + RAgYEmB+8CDEIgEBR0RwSKmzcUwXi1jxqk6t37xO040mr7OkhXnb+3jIQ1trsqjisqg0SeOMaDrb + 98FvS6mIlkUeZ0WurR6izQkZ18QmZxumZE1azrz36CPDbFoW5zfvM59Opy46veihJprfFGpp3y70 + zMDcONXLklsxVZxJvRvoQYwJyedxTrLGaje0SivO9eHQEkXyBu1UVmRrVpfMYGMxaeqEAPSOQXAM + 4ATiseeN3WgYRC6CpsYbxIQxU4F9mkhto+tXpOUTR4fF8KHY1EXbqPacdAWjBTugpEWda7WMe9oH + ZKQkjxlPTHbHA61qvqVMm8UdGvUosOTE0h4BBPr820PoyCpecCWF5Da/OwdYvh1mq8XUlXw8ECSt + tqhmigvjyBk1TTr6BXYp/kQNIQwg9rs+7ej715ueuR7zoQsRJNw338BFLPQ5ESCKUOTDIGGuwJRS + H4jA9T3mIsIhERSLEAYe3u2Fw2l81qb/cMWScz/69G2OTu+u8d38+1v2+h9oeg8OTdFCD//XTe8+ + c9M/xa6fN70Hhn5oms/UsJmHK/P8aqeYs95ijdhJSdVM/tF6z5uB88iWb+fRq1RmUp9A8KIQouL6 + BHSMd3J+u7uUne0k6y4Rv++8OdUAhV1VnPZ0I1kLSsUXsqjtXtoOdEcXZtnbId5OcEdI9ecBm9Q1 + Q3TvvtTeqob9G1MvrffNON9oV+u0tWImq7LW3KJfD/7OQLLVaKPtouhcGYfxrEibm9wBK/tWp9+x + a3bhIzadzkZ4tPoBSH6ovisKAAA= headers: content-encoding: [gzip] - content-length: ['758'] + content-length: ['761'] content-type: [application/json] status: {code: 200, message: OK} - request: @@ -456,9 +456,9 @@ interactions: response: body: {string: !!python/unicode "{\n \"customers\": [\n {\n \"name\": null,\n \"links\": {\n \"source\": null,\n \"destination\": - null\n },\n \"updated_at\": \"2014-07-01T11:33:11.757331Z\",\n \"created_at\": - \"2014-07-01T11:33:11.640235Z\",\n \"dob_month\": null,\n \"id\": - \"CU3L5OJCUjErSojbUKCxEaGR\",\n \"phone\": null,\n \"href\": \"/customers/CU3L5OJCUjErSojbUKCxEaGR\",\n + null\n },\n \"updated_at\": \"2014-07-01T15:44:42.971621Z\",\n \"created_at\": + \"2014-07-01T15:44:42.833241Z\",\n \"dob_month\": null,\n \"id\": + \"CU4Peo8BtJifohKDry4aff4Z\",\n \"phone\": null,\n \"href\": \"/customers/CU4Peo8BtJifohKDry4aff4Z\",\n \ \"merchant_status\": \"no-match\",\n \"meta\": {\n \"seq\": \"5\"\n },\n \"dob_year\": null,\n \"address\": {\n \"city\": null,\n \"line2\": null,\n \"line1\": null,\n \"state\": @@ -483,35 +483,35 @@ interactions: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU3L5OJCUjErSojbUKCxEaGR + uri: https://api.balancedpayments.com:443/customers/CU4Peo8BtJifohKDry4aff4Z response: body: string: !!binary | - H4sIAAAAAAAAA41UwY7TMBC971dEOdNN07RU6rVaIQESEtALCEUT2yjeTexiO2irKv/OuE1S1w6Y - Sw4z7z173rz4/JAkKem0kS1TOt0l37GQJOfLF1sCWoZV0TXNm7HWcPFioSMIYVp2ivhArFOmDRdg - uBSDyiDST2rdkYJhtASDiHS1zNeL5XaxzL/m+a4odnn+uN1siyL/lk4UoliE8na9XBUbh0JlVbZS - mNofhlN77P5QfNx8er8/PD+pL/K5OnzYvz7Bu8+3M4+1FP6Aaa3YT0vPJgezuBIaTWoQptQGTGeN - TIVctGBIfTuuZQY8i9kvC92kgYN2thMD5Y8GlCqmvU0Rbk4eEPeEK2Wr+XIelu3FfS9Q5Cix0ZRE - 0pkmkZ0w6uR2g0GqTuM9tC7nUqe1KBvQZu2PyVrgTVDkd4Hr8awfNj92Uie8t+g/ThFOM3Ttkmed - nafFDv1+WJFDJKBoWcuGXlbpRMEhc9pnDu6aZEejAvFSArmYFJG5hwZKlFU8JjFgZrju3/oXH5xf - esYM9mqYEpiC/xsmhAeXsrZFLLlCAibl+tgZFiFPqIBvFAgNxD5eEY07ZKCDj0QnYjOMoBn2b3yZ - oYlcQbERFihIRa9v+z/COWACLj61NJqnEYT/V//Q/wEveU2HUwYAAA== + H4sIAAAAAAAAA41UsW7bMBDd8xWC5jqyFKZJPaadkiVDu7QohDNJQ0Qk0iWpoIahf+/RlhSaVMos + Go7vPfHePd7xKsty2hurOq5Nvsl+YSHLjqcvHknoOFZl37afplor5IuDTiCEGdVrGgKxzrixQoIV + So4qo8gwq/V7BpazGiwi8mpdktX6brUuv5e3G0I2pLr+cld+rsqf+Uyhmico9zc3FfEpTG3rTknb + hM0I5n779Qd55ur+wT6KnWqevukDgd2OeP/cN0qGDeaN5jtHL2YHi7QSGk0bkLY2FmzvjMylWnVg + afPWYsctBBbzPw56m0cOut4OHHTYGjCmuQkmRYU9BECcE46UV8vlMi67i4deoMhe4UFbU8UWDqnq + pdUH/zRqZNsbvIcx9VLqjJF1C8aSsE3egWijorgI3ID/+u3y4zr1wvsW/es5wnmBrp3ybIrjPNjx + fBhH5BEpaFY3qmWnUXpR8MiCDYWHOyfZ09iCfKmBnkxKyFxCIyXGtyIlMWIWuP5rfccH70kvmMH/ + Wq4lpuBjzcTw6FLOtoQlZ0jEZMLse8sT5BkV8a0GaYC65ZXQuEBGOrgkepnqYQItsF9xM0ObuILm + EyxSUJqdd/t/wjliIi6uWpbM0wTC9zVcDf8AHOi05VMGAAA= headers: content-encoding: [gzip] - content-length: ['496'] + content-length: ['491'] content-type: [application/json] status: {code: 200, message: OK} - request: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU3L5OJCUjErSojbUKCxEaGR/cards?limit=10&offset=0 + uri: https://api.balancedpayments.com:443/customers/CU4Peo8BtJifohKDry4aff4Z/cards?limit=10&offset=0 response: body: {string: !!python/unicode "{\n \"cards\": [],\n \"meta\": {\n \"last\": - \"/customers/CU3L5OJCUjErSojbUKCxEaGR/cards?limit=10&offset=0\",\n \"next\": - null,\n \"href\": \"/customers/CU3L5OJCUjErSojbUKCxEaGR/cards?limit=10&offset=0\",\n + \"/customers/CU4Peo8BtJifohKDry4aff4Z/cards?limit=10&offset=0\",\n \"next\": + null,\n \"href\": \"/customers/CU4Peo8BtJifohKDry4aff4Z/cards?limit=10&offset=0\",\n \ \"limit\": 10,\n \"offset\": 0,\n \"previous\": null,\n \"total\": - 0,\n \"first\": \"/customers/CU3L5OJCUjErSojbUKCxEaGR/cards?limit=10&offset=0\"\n + 0,\n \"first\": \"/customers/CU4Peo8BtJifohKDry4aff4Z/cards?limit=10&offset=0\"\n \ },\n \"links\": {}\n}"} headers: content-length: ['364'] @@ -521,46 +521,46 @@ interactions: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2RCigJCqQDdYQEcThv3Jxr + uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd response: body: string: !!binary | - H4sIAAAAAAAAA41Uy27bMBC85ysEnevIkp028NXtJbcU6aEtCmFNshURiXT4CGIY+vcubUmmSaXs - RYfdmSF3dsTjTZblxGojO6Z0vsl+YiHLjqcvtgR0DKvCtu2HsdZy8eygIwhhWlpFQiDWKdOGCzBc - ikFlEOknNbunYBitwSAir5blerH8tFiWT2W5WVWbqrpd35X398sf+UQhiiUoq2p993HlUajc1Z0U - pgmH4dQdu/1Wfd3yPw/bl8fP9PvjF/LUvK4e3tTlzH0jRThg3ij229GLycEirYRGkwaEqbUBY52R - uZCLDgxpLsd1zEBgMXtx0DKPHHSzHRiocDSgVDEdbIpwcwiAuCdcKavmy2VcdhcPvUCRvcRGWxNJ - Z5pEWmHUwe9Gg+ysxntoXc+lTmtRt6DNOhyTdcDbqMivAtfjWb9cftykXngv0b+dIpwX6Nopz7o4 - Tosd+v2wIo9IQNG6kS09rdKLgkfmtC883DnJnsYOxHMN5GRSQuYaGilRtuMpiQEzw/X/1nd88H7p - GTPYm2FKYAr+b5gYHl3K2Zaw5AyJmJTrvTUsQZ5QEd8oEBqIe7wSGlfISAcfCStSM4ygGfYrvszQ - Jq6g2AiLFKSi57f9H+EcMBEXn1qazNMIwv+rv+n/AoxtLhJTBgAA + H4sIAAAAAAAAA41Uy27bMBC85ysEnWvLipQ29i1oby2CAG3RokUh0CQLEZFIhY+ggqF/79KWZJpU + wlx02J0ZcmdHPFwlSYqN0qKlUqW75DcUkuRw/EKLo5ZClZumeTfVGsYfLXQCAUwJI7EPhDqhSjOO + NBN8VBlFhlnNdARpSiqkAZFeb/Jytfmw2uTf8ptdWezKcp0Xxfvb7a90pmBJX6UU622+uSlKh0LE + vmoF17U/DCP22I/fi88/Sf98//UT7b88FN3dD7mtn8j5zK4W3B8wrSX9a+nZ7GAWVwKjcY24rpRG + 2lgjUy5WLdK4Ph/XUo08i+mTheZp4KCdradI+qMhQiRV3qYw070HhD3BSun1cjkPy/bivhcg0glo + NBUWZKGJheFa9m43GGRvFNxDqWopdUrxqkFKl/6YtEWsCYrsInADnPXH5sdO6oT3HP31HOE0A9eO + eVbZYV7s2B/GFTlEjCSpatGQ4yqdKDhkRobMwZ2S7GjsEX+sED6aFJG5hAZKhO5ZTGLELHDdv/UF + H5xfesEM+k9TySEFbxsmhAeXsrZFLDlBAiZhqjOaRsgzKuBribhC2D5eEY0LZKADj4ThsRkm0AL7 + GV5m1ESuIOkECxSEJKe3/ZVwjpiAC08tieZpAsH/NVwN/wHQiK4AUwYAAA== headers: content-encoding: [gzip] - content-length: ['495'] + content-length: ['499'] content-type: [application/json] status: {code: 200, message: OK} - request: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU2RCigJCqQDdYQEcThv3Jxr/cards?limit=10&offset=0 + uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd/cards?limit=10&offset=0 response: body: string: !!binary | - H4sIAAAAAAAAA6VVyW7bMBC95ysIHXpKbIlyvAFB4ThBigRx9gJNUQg0F5uITDok5dgI/O8lKcmS - sqBFy4MAzcKZefNm+LoHQICRIjoYgp/2B4BX/7XilIsnJy4FzjLTRi6ostJg/ABvx3x2Pn6+OSE/ - bk7x/XwVn69VUPhv98uLGBczqpaKC+Mc+7jHwg6M4+4hYv0+jmncxSyEvU6XYtTtMIJZ3CUEQxqx - eGDNKR6wmDESHiLY7QW7i/FqlSiqs9TdK7I03WlEtpjmaa5rJ7Kn8kYrnSylNihNFsjguctNyEpP - 10uukOFSJAspjNNH4S7CghrUBEfRmbX10EjBMu1+SnOHHTebxMgXb1FXIEJsETqBTnEs1wC+h5AT - f+8Y3p1dn0yujx9CeXUxRVQPzPO4VhNGhs6k2jhraeYWgl3CZrOkTowVJdw0QSzrb2A4ReIpEWjh - vc6vL69uz0YTMP42ujsFx6PJxT6YtEatJp7aKEpNheeG6spgqpDwdXznGlXibEls1iRBnh0wjDoH - Ye8gjO6jaBjDIYxb/X4fdgaPtVA5ZNa+Tk4LsJXUa8hZTB2yH4gjFy+CMbhEXIA7n3q9MZYaxhf/ - YT8L6mBJvM0ghFFY98YyE0ZtksLAxX83GRiJhNCpbccQGJXRir857I1+1Pi4ocjNIAxhRUg7DG/K - DLhOVlRxxqmDvRHAkX83OkFeOyiYCGybQF4ecNkDIoGQBniW1Brusi/YNAQMpbrKf64oc6i0/W5p - /wVxFf0DCWC/04l7j/lsbC2UvxxaQWMOgxRpz6J2uals7E/2VJ7a15QvuDmKwi+SMU3NUdnDQNB1 - c60EVVHFGvz3y31Um2i5UII8upUUDQ2Wiq64zNwCrlgQGGnXlfPLmRIwrv6/YAujX9ZvNn7+LrQI - 18vM2Dmu2vnqu9riZNveaYuEck39nai1ovArtdtiXIpAzjWZy9S/RSVzaqFq+kYwP0Cf+BQ6V+He - 9jfXVB+z7QYAAA== + H4sIAAAAAAAAA6VVW2+bMBR+76+weNhTmwBOyEWqprSbVq1NWq1dO3WakIPtxiqxmW2yoCr/fbaB + AL1o08YDEud+vvOdw9MBAF6CJFbeFHw3HwA8ubcRp4w/WnEtsJa50mJNpJF6p1/h+TdcbBbXH0hx + cQWz2Z2crH5ir/LfHdaBKOMPRGaScW0dx8mI+oMQwmiI6HicQAKjhPrhaBCRBEUDihMKI4yTkAQU + Tow5SSYUUor9IQqjkbcPnGw2sSQqT21cnqfpXsPz9bIsc9t6AvM03mij4kwojdJ4jXSysrVx0ejJ + NmMSaSZ4vBZcW33g7zOsiUZdcCR5MLYOGsFpruxHbW6xY7qItfjlLNoKhLFpQsWhVZyILQhfQsiw + i3sK52cn6i7anKELqPKLW3xzu7yctBBBmjwIWVhroVcGgn3BusiIFSeSYKa7INb9dzBcIv4Yc7R2 + Xp+v5pdfPs0W4PRsdv0RnMwW54dg0Zv1ungqLQnRDZ4FUY3BUiLu+rhlCjXiPMOmahwjx47QDwZH + /ujID26C4XQAp4NhbzwMQhjct1KVkBn7NjkNwEbS7qFkMbHIviIObD4TGcwR4+Dald4ejKGGds2/ + Os+KOonAzmbih4Hf9k5EzrUs4srA5n+xGQniMSZLM44p0DInDX9L2DvzaPGxIMjuYOiHDSHNMjxr + 02Mq3hDJKCMW9k4CS/796nhl76BiIjBjAmV7wFYPsABcaOBY0hq4rb5i0xRQlKqm/pUk1KLSd7el + /xfEleQPJBhEcBLA+3I3dgbKHxYtr7OHXoqUY1G/vlQm9xt3qiztfcrWTB8H/jtBqSL6uJ6hx8m2 + e1a8pqnqDP57cJfVFFofFK/MbiTVQL1Mkg0TuT3ADQs8Lcy5sn4lUzzK5P83bGB0x/rZxS//Cz3M + VJZrs8fNOJ/cVHsM7/p7bVVQqWn/J1qjqPxq7a5alyqRdY1XInX/opo5rVQtfSeZW6A3fCqd7fBg + 9xv4QHhu7QYAAA== headers: content-encoding: [gzip] content-length: ['751'] @@ -570,17 +570,17 @@ interactions: body: '{"amount": 1000, "description": "janet"}' headers: {} method: POST - uri: https://api.balancedpayments.com:443/cards/CC2SGPDNPBU0oOKbaes9tqC1/debits + uri: https://api.balancedpayments.com:443/cards/CC3MHBsW6vHaL3suLVdTVbO9/debits response: body: {string: !!python/unicode "{\n \"debits\": [\n {\n \"status\": \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"customer\": - \"CU2RCigJCqQDdYQEcThv3Jxr\",\n \"source\": \"CC2SGPDNPBU0oOKbaes9tqC1\",\n + \"CU3KXdyvNSDeyLP3pAWr9hqd\",\n \"source\": \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \ \"order\": null,\n \"dispute\": null\n },\n \"updated_at\": - \"2014-07-01T11:33:14.816252Z\",\n \"created_at\": \"2014-07-01T11:33:13.722996Z\",\n - \ \"transaction_number\": \"W840-430-2910\",\n \"failure_reason\": + \"2014-07-01T15:44:45.548561Z\",\n \"created_at\": \"2014-07-01T15:44:44.926318Z\",\n + \ \"transaction_number\": \"W260-348-1523\",\n \"failure_reason\": null,\n \"currency\": \"USD\",\n \"amount\": 1000,\n \"failure_reason_code\": - null,\n \"meta\": {},\n \"href\": \"/debits/WD3NqPJy6Kz9Xxn4NCgfjq1r\",\n - \ \"appears_on_statement_as\": \"BAL*example.com\",\n \"id\": \"WD3NqPJy6Kz9Xxn4NCgfjq1r\"\n + null,\n \"meta\": {},\n \"href\": \"/debits/WD4RA4ycTczpRdM0e3NHNYi5\",\n + \ \"appears_on_statement_as\": \"BAL*example.com\",\n \"id\": \"WD4RA4ycTczpRdM0e3NHNYi5\"\n \ }\n ],\n \"links\": {\n \"debits.customer\": \"/customers/{debits.customer}\",\n \ \"debits.dispute\": \"/disputes/{debits.dispute}\",\n \"debits.source\": \"/resources/{debits.source}\",\n \"debits.order\": \"/orders/{debits.order}\",\n diff --git a/tests/py/fixtures/TestClosing.yml b/tests/py/fixtures/TestClosing.yml index dce6759c81..81665809eb 100644 --- a/tests/py/fixtures/TestClosing.yml +++ b/tests/py/fixtures/TestClosing.yml @@ -7,9 +7,9 @@ interactions: response: body: {string: !!python/unicode "{\n \"customers\": [\n {\n \"name\": null,\n \"links\": {\n \"source\": null,\n \"destination\": - null\n },\n \"updated_at\": \"2014-07-01T11:33:23.610868Z\",\n \"created_at\": - \"2014-07-01T11:33:23.506527Z\",\n \"dob_month\": null,\n \"id\": - \"CU3Yrhw41eTJjqJBFTTVCXTr\",\n \"phone\": null,\n \"href\": \"/customers/CU3Yrhw41eTJjqJBFTTVCXTr\",\n + null\n },\n \"updated_at\": \"2014-07-01T15:44:57.039650Z\",\n \"created_at\": + \"2014-07-01T15:44:56.925624Z\",\n \"dob_month\": null,\n \"id\": + \"CU5555ODX3LB3yvScKR27QPD\",\n \"phone\": null,\n \"href\": \"/customers/CU5555ODX3LB3yvScKR27QPD\",\n \ \"merchant_status\": \"no-match\",\n \"meta\": {\n \"seq\": \"6\"\n },\n \"dob_year\": null,\n \"address\": {\n \"city\": null,\n \"line2\": null,\n \"line1\": null,\n \"state\": @@ -41,13 +41,13 @@ interactions: \"321174851\",\n \"bank_name\": \"SAN MATEO CREDIT UNION\",\n \"account_type\": \"checking\",\n \"name\": \"Alice G. Krebs\",\n \"links\": {\n \"customer\": null,\n \"bank_account_verification\": null\n },\n \"can_credit\": - true,\n \"created_at\": \"2014-07-01T11:33:24.291792Z\",\n \"fingerprint\": + true,\n \"created_at\": \"2014-07-01T15:44:57.851974Z\",\n \"fingerprint\": \"5f0ba9fa3f1122ef13b944a40abfe44e7eba9e16934e64200913cb4c402ace14\",\n \"updated_at\": - \"2014-07-01T11:33:24.291795Z\",\n \"href\": \"/bank_accounts/BA3ZjVKBXZkadShuLO55Gx0R\",\n + \"2014-07-01T15:44:57.851976Z\",\n \"href\": \"/bank_accounts/BA567z1OsbkUtySKaCLl1QdX\",\n \ \"meta\": {},\n \"account_number\": \"xxxxxx0001\",\n \"address\": {\n \"city\": null,\n \"line2\": null,\n \"line1\": null,\n \ \"state\": null,\n \"postal_code\": null,\n \"country_code\": - null\n },\n \"can_debit\": false,\n \"id\": \"BA3ZjVKBXZkadShuLO55Gx0R\"\n + null\n },\n \"can_debit\": false,\n \"id\": \"BA567z1OsbkUtySKaCLl1QdX\"\n \ }\n ],\n \"links\": {\n \"bank_accounts.credits\": \"/bank_accounts/{bank_accounts.id}/credits\",\n \ \"bank_accounts.bank_account_verifications\": \"/bank_accounts/{bank_accounts.id}/verifications\",\n \ \"bank_accounts.customer\": \"/customers/{bank_accounts.customer}\",\n @@ -64,93 +64,93 @@ interactions: - request: body: '{"routing_number": "321174851", "bank_name": "SAN MATEO CREDIT UNION", "account_type": "checking", "name": "Alice G. Krebs", "links": {"customer": - "/customers/CU3Yrhw41eTJjqJBFTTVCXTr"}, "can_credit": true, "created_at": "2014-07-01T11:33:24.291792Z", + "/customers/CU5555ODX3LB3yvScKR27QPD"}, "can_credit": true, "created_at": "2014-07-01T15:44:57.851974Z", "fingerprint": "5f0ba9fa3f1122ef13b944a40abfe44e7eba9e16934e64200913cb4c402ace14", - "updated_at": "2014-07-01T11:33:24.291795Z", "meta": {}, "account_number": "xxxxxx0001", + "updated_at": "2014-07-01T15:44:57.851976Z", "meta": {}, "account_number": "xxxxxx0001", "address": {"city": null, "line2": null, "line1": null, "state": null, "postal_code": - null, "country_code": null}, "can_debit": false, "id": "BA3ZjVKBXZkadShuLO55Gx0R"}' + null, "country_code": null}, "can_debit": false, "id": "BA567z1OsbkUtySKaCLl1QdX"}' headers: {} method: PUT - uri: https://api.balancedpayments.com:443/bank_accounts/BA3ZjVKBXZkadShuLO55Gx0R + uri: https://api.balancedpayments.com:443/bank_accounts/BA567z1OsbkUtySKaCLl1QdX response: body: string: !!binary | - H4sIAAAAAAAAA5VTW2+bMBR+769APK/BBlNK3pKsq9puidTSqss0IWMOixsCmTFdo4j/PkOgXJpq - HQ9+8Hc4/i7n7E80TQ9osvYpY2meyEwfaz/Upabtq1PBIs0lT375Sb4JQChct0yMHXJuY/1TU1T1 - SOgGSvxuMte+TbyLhTa7vfh85Wn386vFvC2u3/LlblvVsxWwtXqirWg6TWLOQLscaTcCgqzFY56s - S6oNSUWT5ZlMNweCs3vru1j9IRi866ff19Mvnvcwe/TEa4OBav8ZBI84o5KniWqb5HFcKyteJTKa - +ExAyKWqkCKHFhFAJYQ+LRHdRJicIucUYQ/jsWWNTTIyXey45rIVECm5ILaCJ9U/doQC6kbUijA2 - TYiwFbiEUIJoEAEh4ICCAZ+5FoEzYiLkYosFhBFkUgaYtI3zbfgPLo5zjpHd4bISEJUkjN4gGNOJ - tXx6uJk+Ltc0vFvlXxe2ffmCbtu3NiBpGULrUZNsOysv1YcQ6gwLDUMB2TA/Lne19Y2xKiUVNJjH - r/Hb60wq7W+vt6kCYp+l4RGwGnux66JHow8hqJKPaJy10fOwdO5dq6pOhTp/lppKNZ2x7S/e6DBb - pSvDJPa9YEY8LIym+GDVoFO3vDfaH2ze3YZm6YZkO9tmNKuXGQOmDVDUMzNoUln6QU517f/prbzs - qRkyfNeqQleBFSfFX3VUwNQiBQAA + H4sIAAAAAAAAA5VTTW+jMBC991cgzm1ig4GSW5pUqyrdZNskUrXVChkzbK0QExlTLY3472tIKISm + qy4HH/yG8fuY2V8YhhlSsQkoY2kuVGaOjGd9aRj7+tSwTHPFxe9A5NsQpMZN28LYI9cONi+borqH + oFuo8OV4bnwfr24XxuTxdnq3Mtbzu8W8LT6+FahiV9ezF2Ab/URb0XQaJ5yB8W1gzCSEWYsnXGwq + qg1JTZPlmUq3B4KTtaO/xfTJvr+xi9clmz1a3sOP6XuDnurgFSSPOaOKp0K3FXmSHJWV7xIZFQGT + EHGlK5TMoUUkUAVRQCvEtBAmV8i7QniFnREhI8cbaK98j/xsBcRaLsid5KL+x4lRSP2Y2jHGlgUx + tkOfEEoQDWMgBDzQMGDXtwm4xELIxzYLCSPIogwwaRvnu+ifXK4Hluu6yO1weZEQVySGJ4MwvBk7 + rveGF1m4WatiOaOT+wQ/RE/tW1tQtAqh9ahJtp2VP/WHEOoMC40iCVk/P66Ko/WNsTolHTRY56/x + x+tMae0fr3epBpKApdEZsB57WXTRs9FHENbJxzTJ2uh5VDn3qVV1p1KfvypNlZrO2J4u3uAwW5Ur + /ST2J8EMeFQOm+KDVb1O3fKT0f5i8+42NEvXJ9vZtmGzetmwx7QByuPM9JrUln6R07H2//TWXp6o + 6TP81KrS1IGVF+VfDeyr4SIFAAA= headers: content-encoding: [gzip] - content-length: ['534'] + content-length: ['533'] content-type: [application/json] status: {code: 200, message: OK} - request: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU3Yrhw41eTJjqJBFTTVCXTr + uri: https://api.balancedpayments.com:443/customers/CU5555ODX3LB3yvScKR27QPD response: body: string: !!binary | - H4sIAAAAAAAAA41Uy27bMBC85ysEnetIsh0n8DEBeshZLfpAIdDkFmIikQ5JtTUM/XuXtiTTpFL2 - osPuzJA7O+LxJklS2mkjW1A63SbfsZAkx9MXW4K0gFXRNc2HsdZw8WqhIwhhWnaK+kCsM9CGC2K4 - FIPKINJPat2eEQOsIgYR6TIv1ov8fpEXZVFsV6vtcnW7KfKHzcO3dKJQBRHKXb65W947FCZ3VSuF - qf1hOLPHPn1afVX173UB5fPL2/Pjx7L8/PSlVJcz97UU/oBpreCnpWeTg1lcCY2mNRGm0oaYzhqZ - CrloiaH15bgWDPEshjcL3aSBg3a2AxDlj0YYU6C9TVFuDh4Q94QrheV8uQjL9uK+Fyiyl9hoKirZ - TJPKThh1cLvBILtO4z20ruZSp7WoGqLN2h8TWsKboMivAtfjWT9sfuykTngv0b+dIpxm6Nopzzo7 - Tosd+v2wIodIiWJVLRt2WqUTBYfMWZ85uHOSHY0dEa8VoSeTIjLX0ECJwY7HJAbMDNf9W9/xwfml - Z8yAPwaUwBT83zAhPLiUtS1iyRkSMBnX+85AhDyhAr5RRGhC7eMV0bhCBjr4SHQiNsMImmH/wpeZ - NJErKBhhgYJU7Py2/yOcAybg4lPLonkaQfh/9Tf9XxvCezBTBgAA + H4sIAAAAAAAAA41UTY+bMBS8769AnJtACCTaHLt7a6V+S1WrCjm2K6wFO/XHqlHEf+9zAsSx6Xo5 + cHieGfvNG/t0lyQpNkqLjkqV7pKfUEiS0/kPSxx1FKrctO2bsdYy/mShIwhgShiJfSDUCVWacaSZ + 4IPKINJPauZAkKakRhoQaZGvykW+XeSrr6tqV5a7arvM1/ebKv+RThQs6YuUzfK+qDZF6VCI2Ned + 4Lrxm2HEbvvwrYLvw+P39fu36+PzF/zuc7H99PHxuuehEdxvMG0k/W3p2eRgFlcCo3GDuK6VRtpY + I1MuFh3SuLlu11GNPIvpHwvdpIGDtrcjRdJvDREiqfImhZk+ekCYE4yUFvPlVVi2B/e9AJGDgIW2 + xoLMLGJhuJZHdzVoZG8UnEOpei51SvG6RUqXfpu0Q6wNiuwmcD3s9cvmx3bqhPca/eUU4TQD1855 + VtlpGuyw3g8jcogYSVI3oiXnUTpRcMiM9JmDuyTZ0dgj/lQjfDYpInMLDZQI3bOYxICZ4bq39T8+ + OFd6xgz6V1PJIQWvayaEB4eytkUsuUACJmHqYDSNkCdUwNcScYWwfbwiGjfIQAceCcNjPYygGfYz + vMyojRxB0hEWKAhJLm/7C+EcMAEXnloSzdMIgvvV3/X/AG22s3RTBgAA headers: content-encoding: [gzip] - content-length: ['495'] + content-length: ['498'] content-type: [application/json] status: {code: 200, message: OK} - request: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU3Yrhw41eTJjqJBFTTVCXTr/bank_accounts?limit=10&offset=0 + uri: https://api.balancedpayments.com:443/customers/CU5555ODX3LB3yvScKR27QPD/bank_accounts?limit=10&offset=0 response: body: string: !!binary | - H4sIAAAAAAAAA61U70/bMBD9zl8R5cM+jdZOXEoroakwhoCNShAQY5oix7mspmnSOQ6jqvK/z3YT - 8oNWYxL9EKl357t37z3des+y7IAmc58yluaJzOyx9UMFLWttviot0lzy5Jef5IsAhMrbroPxkBwO - sP2xKjI9EroAnb+ZXFnfJt7p1Dq5Pv187lm3V+fTq7q4nOXL1dLUsxmwuRpRV1SdJjFnYJ31rEsB - QVbnY57MNdQKpILJ8kymiw3Ak1v3u5j9IRi8i8ffF8dfPO/u5N4TLw06W/tPIHjEGZU8TVTbJI/j - crPiZUVGE58JCLlUFVLkUGcEUAmhT3XGdhAm+2i4j7CH8dh1xw7pOSM8HDkP9QKRWhfEUvDEvBlE - KKCjiLoRxo4DEXaDESGUIBpEQAgMQaUBH4xcAgfEQWiEXRYQRpBDGWBSN86X4T+wDIeHGA0aWGYC - Ig2i3zJC/3jiPjzeXR7fP8xpeDPLv04Hg7NndF3PWoCkWoSao0rZ2ivP5ocQapiFhqGArKsfl6uS - +opYpZISGpztYfw6nEm1++vwMlWJ2GdpuCVpbC9WzexW6UMIjPIRjbNaeh5q5nZSZToV6vtT72RX - hJmwHdPMiN+vrJv1dxm3Lc2nmC+4PMLoQxpFGcgjVGpiJ/CsW2r/bki0X8R9vyFmupqCUTljg0JF - qsBSwBNPc61wA4pMlQr6Xfks4uL9CFCUGhtqxzROQ/u49YyIGlbX7OuW93s8LPplbQm2nW/+ax2P - N/Zu3pvqrHWgNu9ZwyEdoFVVUTmgjXNzr94Iqir+v40Nma19+h2MO8kqbK3aXvEXbKzmtoYGAAA= + H4sIAAAAAAAAA61U22rbQBB9z1cIPfSpsXaltRQHQnEulJDUbmIHQksRq9WoWSxLZrUycYP+vbtr + KbokKSlEDwLNjGbOnHOYpwPLsiOarULKWF5msrCPrZ8qaFlP5q3SIi8lz36HWbmOQKi87bkYB+Ro + jO3PTZHpkdE16PxiOrO+TZcXc+vs9uL8cmndzS7ns7a4nhXK3cbUswdgKzWirWg6TVPOwPo6sq4E + REWbT3m20lAbkAomKwuZr/cAz+7G6pmf33vXp95uu2BXt25w8/38ucFg63ALgiecUcnzTLXNyjSt + N6ueV2Q0C5mAmEtVIUUJbUYAlRCHVGdsF2FyiIJDhJd4fEzI8TgYKa4mAfnRLpCodUFsBM/MP+ME + RXSSUC/B2HUhwV40IYQSRKMECIEAVBqwP/EI+MRFaII9FhFGkEsZYNI2LjfxP7EcjVzf95HfwfIg + INEgnJ4RnNPp2A/+4HkRre7kbnFFz65TfBPft7PWIKkWoeWoUbb1yqN5EEIds9A4FlAM9eNyV1Pf + EKtUUkKD+3oYvwwXUu3+MrzJVSINWR6/kjS2F7tu9lXpY4iM8glNi1Z6Hmvm3qTKdKrU+5feyW4I + M2E7pYUR32msWzhvGbcvzZeUr7k8wehTniQFyBNUa2Jn8Khbav/uSbSfxf24IWa6moJRPWOPQkWa + wEbAluelVrgDReZKBf1f/VvCxccRoCg1NtSO6ZyG/nEbGRE1rKHZn3reH/G4curaGmw/3/3qHY93 + 9u7em+asDaB271nHIQOgTVXVOKCPc3+v3gmqKf6/jQ2ZvX2cAcY3yapsrdpB9RchlguzhgYAAA== headers: content-encoding: [gzip] - content-length: ['626'] + content-length: ['625'] content-type: [application/json] status: {code: 200, message: OK} - request: body: '{"amount": 1000.00, "description": "alice"}' headers: {} method: POST - uri: https://api.balancedpayments.com:443/bank_accounts/BA3ZjVKBXZkadShuLO55Gx0R/credits + uri: https://api.balancedpayments.com:443/bank_accounts/BA567z1OsbkUtySKaCLl1QdX/credits response: body: {string: !!python/unicode "{\n \"credits\": [\n {\n \"status\": \"pending\",\n \"description\": \"alice\",\n \"links\": {\n \"customer\": - \"CU3Yrhw41eTJjqJBFTTVCXTr\",\n \"destination\": \"BA3ZjVKBXZkadShuLO55Gx0R\",\n - \ \"order\": null\n },\n \"updated_at\": \"2014-07-01T11:33:26.890394Z\",\n - \ \"created_at\": \"2014-07-01T11:33:26.464040Z\",\n \"transaction_number\": - \"CR317-809-4898\",\n \"failure_reason\": null,\n \"currency\": + \"CU5555ODX3LB3yvScKR27QPD\",\n \"destination\": \"BA567z1OsbkUtySKaCLl1QdX\",\n + \ \"order\": null\n },\n \"updated_at\": \"2014-07-01T15:45:00.125855Z\",\n + \ \"created_at\": \"2014-07-01T15:44:59.861937Z\",\n \"transaction_number\": + \"CR401-922-8833\",\n \"failure_reason\": null,\n \"currency\": \"USD\",\n \"amount\": 1000,\n \"failure_reason_code\": null,\n - \ \"meta\": {},\n \"href\": \"/credits/CR41Koc50vYjzRVQM3AEPqUB\",\n - \ \"appears_on_statement_as\": \"example.com\",\n \"id\": \"CR41Koc50vYjzRVQM3AEPqUB\"\n + \ \"meta\": {},\n \"href\": \"/credits/CR58nuI5mbLeeWVDYO4ewvQB\",\n + \ \"appears_on_statement_as\": \"example.com\",\n \"id\": \"CR58nuI5mbLeeWVDYO4ewvQB\"\n \ }\n ],\n \"links\": {\n \"credits.order\": \"/orders/{credits.order}\",\n \ \"credits.customer\": \"/customers/{credits.customer}\",\n \"credits.destination\": \"/resources/{credits.destination}\",\n \"credits.reversals\": \"/credits/{credits.id}/reversals\",\n diff --git a/tests/py/fixtures/TestPayday.yml b/tests/py/fixtures/TestPayday.yml new file mode 100644 index 0000000000..83db00b89d --- /dev/null +++ b/tests/py/fixtures/TestPayday.yml @@ -0,0 +1,286 @@ +interactions: +- request: + body: null + headers: {} + method: GET + uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd + response: + body: + string: !!binary | + H4sIAAAAAAAAA41Uy27bMBC85ysEnWvLipQ29i1oby2CAG3RokUh0CQLEZFIhY+ggqF/79KWZJpU + wlx02J0ZcmdHPFwlSYqN0qKlUqW75DcUkuRw/EKLo5ZClZumeTfVGsYfLXQCAUwJI7EPhDqhSjOO + NBN8VBlFhlnNdARpSiqkAZFeb/Jytfmw2uTf8ptdWezKcp0Xxfvb7a90pmBJX6UU622+uSlKh0LE + vmoF17U/DCP22I/fi88/Sf98//UT7b88FN3dD7mtn8j5zK4W3B8wrSX9a+nZ7GAWVwKjcY24rpRG + 2lgjUy5WLdK4Ph/XUo08i+mTheZp4KCdradI+qMhQiRV3qYw070HhD3BSun1cjkPy/bivhcg0glo + NBUWZKGJheFa9m43GGRvFNxDqWopdUrxqkFKl/6YtEWsCYrsInADnPXH5sdO6oT3HP31HOE0A9eO + eVbZYV7s2B/GFTlEjCSpatGQ4yqdKDhkRobMwZ2S7GjsEX+sED6aFJG5hAZKhO5ZTGLELHDdv/UF + H5xfesEM+k9TySEFbxsmhAeXsrZFLDlBAiZhqjOaRsgzKuBribhC2D5eEY0LZKADj4ThsRkm0AL7 + GV5m1ESuIOkECxSEJKe3/ZVwjpiAC08tieZpAsH/NVwN/wHQiK4AUwYAAA== + headers: + content-encoding: [gzip] + content-length: ['499'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: null + headers: {} + method: GET + uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd/cards?limit=10&offset=0 + response: + body: + string: !!binary | + H4sIAAAAAAAAA6VVW2+bMBR+76+weNhTmwBOyEWqprSbVq1NWq1dO3WakIPtxiqxmW2yoCr/fbaB + AL1o08YDEud+vvOdw9MBAF6CJFbeFHw3HwA8ubcRp4w/WnEtsJa50mJNpJF6p1/h+TdcbBbXH0hx + cQWz2Z2crH5ir/LfHdaBKOMPRGaScW0dx8mI+oMQwmiI6HicQAKjhPrhaBCRBEUDihMKI4yTkAQU + Tow5SSYUUor9IQqjkbcPnGw2sSQqT21cnqfpXsPz9bIsc9t6AvM03mij4kwojdJ4jXSysrVx0ejJ + NmMSaSZ4vBZcW33g7zOsiUZdcCR5MLYOGsFpruxHbW6xY7qItfjlLNoKhLFpQsWhVZyILQhfQsiw + i3sK52cn6i7anKELqPKLW3xzu7yctBBBmjwIWVhroVcGgn3BusiIFSeSYKa7INb9dzBcIv4Yc7R2 + Xp+v5pdfPs0W4PRsdv0RnMwW54dg0Zv1ungqLQnRDZ4FUY3BUiLu+rhlCjXiPMOmahwjx47QDwZH + /ujID26C4XQAp4NhbzwMQhjct1KVkBn7NjkNwEbS7qFkMbHIviIObD4TGcwR4+Dald4ejKGGds2/ + Os+KOonAzmbih4Hf9k5EzrUs4srA5n+xGQniMSZLM44p0DInDX9L2DvzaPGxIMjuYOiHDSHNMjxr + 02Mq3hDJKCMW9k4CS/796nhl76BiIjBjAmV7wFYPsABcaOBY0hq4rb5i0xRQlKqm/pUk1KLSd7el + /xfEleQPJBhEcBLA+3I3dgbKHxYtr7OHXoqUY1G/vlQm9xt3qiztfcrWTB8H/jtBqSL6uJ6hx8m2 + e1a8pqnqDP57cJfVFFofFK/MbiTVQL1Mkg0TuT3ADQs8Lcy5sn4lUzzK5P83bGB0x/rZxS//Cz3M + VJZrs8fNOJ/cVHsM7/p7bVVQqWn/J1qjqPxq7a5alyqRdY1XInX/opo5rVQtfSeZW6A3fCqd7fBg + 9xv4QHhu7QYAAA== + headers: + content-encoding: [gzip] + content-length: ['751'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: '{"amount": 1576, "description": "janet"}' + headers: {} + method: POST + uri: https://api.balancedpayments.com:443/cards/CC3MHBsW6vHaL3suLVdTVbO9/debits + response: + body: {string: !!python/unicode "{\n \"debits\": [\n {\n \"status\": + \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"customer\": + \"CU3KXdyvNSDeyLP3pAWr9hqd\",\n \"source\": \"CC3MHBsW6vHaL3suLVdTVbO9\",\n + \ \"order\": null,\n \"dispute\": null\n },\n \"updated_at\": + \"2014-07-01T15:44:48.009128Z\",\n \"created_at\": \"2014-07-01T15:44:47.335350Z\",\n + \ \"transaction_number\": \"W589-042-5118\",\n \"failure_reason\": + null,\n \"currency\": \"USD\",\n \"amount\": 1576,\n \"failure_reason_code\": + null,\n \"meta\": {},\n \"href\": \"/debits/WD4UhNrdn6rW5mo9B8dy3Vad\",\n + \ \"appears_on_statement_as\": \"BAL*example.com\",\n \"id\": \"WD4UhNrdn6rW5mo9B8dy3Vad\"\n + \ }\n ],\n \"links\": {\n \"debits.customer\": \"/customers/{debits.customer}\",\n + \ \"debits.dispute\": \"/disputes/{debits.dispute}\",\n \"debits.source\": + \"/resources/{debits.source}\",\n \"debits.order\": \"/orders/{debits.order}\",\n + \ \"debits.refunds\": \"/debits/{debits.id}/refunds\",\n \"debits.events\": + \"/debits/{debits.id}/events\"\n }\n}"} + headers: + content-length: ['1002'] + content-type: [application/json] + status: {code: 201, message: CREATED} +- request: + body: null + headers: {} + method: GET + uri: https://api.balancedpayments.com:443/customers/CU3O2IKYocPZ5LA7GkZWpwKZ + response: + body: + string: !!binary | + H4sIAAAAAAAAA41UTY/TMBS876+IcqabpJu2orcVB4QWCQ4gRBGKXNsoVhM7+AOoqvx3ntMk69oB + 7yWH55mJ37zxu9wlSYqN0qKlUqX75BsUkuQyfOGIo5ZClZumeTXVGsZPFjqBAKaEkdgHQp1QpRlH + mgk+qowi/axmOoI0JRXSgEjXeVGu8t0qLz4Vm335sC+397tNmW/zQzpTsKQRynazLnavHQoRx6oV + XNd+M4zY3775/PBh/e7pq8AfD5v3j7u3p8OX7veTI9DVgvsNprWkPyw9mx3M4kpgNK4R15XSSBtr + ZMrFqkUa188ttlQjz2L6czAoDRy0vZ0pkn5riBBJlTcpzPTZA8KcYKR0vVwuwrK9uO8FiHQCDpoK + C7JwiIXhWp7d06CRo1FwD6WqpdQpxasGKV36bdIWsSYospvA9fCv7zY/tlMnvM/Rv58jnGbg2pBn + lV3mwY7n/Tgih4iRJFUtGjKM0omCQ2akzxzcNcmOxhHxU4XwYFJE5hYaKBF6ZDGJEbPAdV/rP3xw + nvSCGfSPppJDCl7WTAgPLmVti1hyhQRMwlRnNI2QZ1TA1xJxhbBdXhGNG2SgA0vC8FgPE2iB/Qs2 + M2oiV5B0ggUKQpLrbv9POEdMwIVVS6J5mkDwvvq7/i9xeE/EUwYAAA== + headers: + content-encoding: [gzip] + content-length: ['496'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: null + headers: {} + method: GET + uri: https://api.balancedpayments.com:443/customers/CU3O2IKYocPZ5LA7GkZWpwKZ/bank_accounts?limit=10&offset=0 + response: + body: + string: !!binary | + H4sIAAAAAAAAA61UXW+bMBR9769APOxpDTYfIUSqpnTqsi1R232pW6YJObZpvBBIjWkTRfz32Q4E + SIPWh74gcc/1veeec3V3Z4ZhzlGyDBHGaZ6IzBwav2XQMHb6K2Ge5oIl92GSr+aUS9yENgSuPRjY + 5tsqSddI0Ioq/O5qOv1mfBh9Hd8Yl6PriXE9qjPLRqHYrnUyXlC8lPXrjKrMx3RFufEZbWsoZslS + UazISXo4z4RKVLXe/3Bu7E+TXym+nXnTkT9ezu7WT5PZocDRtOEj5SxiGAmWJrJAksdxOVFxGA2j + JMScEiZkhuA5rRFOkaAkRAoxbQDdc+CfA/gdekPXGbpBD/j9gefV/c1ITkr5mrNEvyEI9z3okoAQ + B0FMfIQD2yOe7wYUOGCAbYlHyPaxB/qIRnOAg7kN+xBGURD0g1qZfE3+w8UDvgeaXBacRoqE1VoA + 63LkfJkm2dX4nmd/H6LFz4Xr3D64pO61ogIpE2qNKlPrHdlsNrbjoIbvhHCaHZvHxLbUvVJVWiRd + pvbpMHwezoQc/Hl4nUogDnFKToB61/m2iZ70ndC5tj1CcVb7zoiSrVMnXamQ3z9qJrNSS4fNGGXa + eava28zq2tq2L+9itmLiAoI3aRRlVFyAUlszoRtVUi3vXkTz4OzrNdHdZRcIyh57FjJSBdacPrI0 + Vw43qIhUuqDelc8ixl9PACmp3kG1MY270L5oPW2ionW86bvW4vcYKawytyTbxpt/rcvxwtrNY5NV + 5rVbNI9ZY0OOiFZZRUcRfaxeSGp/2TrodE6sxWzNYx1x7HxamMq1s+IftGquiHsGAAA= + headers: + content-encoding: [gzip] + content-length: ['620'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: '{"amount": 1500.00, "description": "homer"}' + headers: {} + method: POST + uri: https://api.balancedpayments.com:443/bank_accounts/BA3QLnsEGgrsjqfhXh43Pq4d/credits + response: + body: {string: !!python/unicode "{\n \"credits\": [\n {\n \"status\": + \"succeeded\",\n \"description\": \"homer\",\n \"links\": {\n \"customer\": + \"CU3O2IKYocPZ5LA7GkZWpwKZ\",\n \"destination\": \"BA3QLnsEGgrsjqfhXh43Pq4d\",\n + \ \"order\": null\n },\n \"updated_at\": \"2014-07-01T15:44:49.736053Z\",\n + \ \"created_at\": \"2014-07-01T15:44:49.468347Z\",\n \"transaction_number\": + \"CR096-343-6203\",\n \"failure_reason\": null,\n \"currency\": + \"USD\",\n \"amount\": 1500,\n \"failure_reason_code\": null,\n + \ \"meta\": {},\n \"href\": \"/credits/CR4WGfyhoAK9GlZY845TaPch\",\n + \ \"appears_on_statement_as\": \"example.com\",\n \"id\": \"CR4WGfyhoAK9GlZY845TaPch\"\n + \ }\n ],\n \"links\": {\n \"credits.order\": \"/orders/{credits.order}\",\n + \ \"credits.customer\": \"/customers/{credits.customer}\",\n \"credits.destination\": + \"/resources/{credits.destination}\",\n \"credits.reversals\": \"/credits/{credits.id}/reversals\",\n + \ \"credits.events\": \"/credits/{credits.id}/events\"\n }\n}"} + headers: + content-length: ['955'] + content-type: [application/json] + status: {code: 201, message: CREATED} +- request: + body: null + headers: {} + method: GET + uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd + response: + body: + string: !!binary | + H4sIAAAAAAAAA41Uy27bMBC85ysEnWvLipQ29i1oby2CAG3RokUh0CQLEZFIhY+ggqF/79KWZJpU + wlx02J0ZcmdHPFwlSYqN0qKlUqW75DcUkuRw/EKLo5ZClZumeTfVGsYfLXQCAUwJI7EPhDqhSjOO + NBN8VBlFhlnNdARpSiqkAZFeb/Jytfmw2uTf8ptdWezKcp0Xxfvb7a90pmBJX6UU622+uSlKh0LE + vmoF17U/DCP22I/fi88/Sf98//UT7b88FN3dD7mtn8j5zK4W3B8wrSX9a+nZ7GAWVwKjcY24rpRG + 2lgjUy5WLdK4Ph/XUo08i+mTheZp4KCdradI+qMhQiRV3qYw070HhD3BSun1cjkPy/bivhcg0glo + NBUWZKGJheFa9m43GGRvFNxDqWopdUrxqkFKl/6YtEWsCYrsInADnPXH5sdO6oT3HP31HOE0A9eO + eVbZYV7s2B/GFTlEjCSpatGQ4yqdKDhkRobMwZ2S7GjsEX+sED6aFJG5hAZKhO5ZTGLELHDdv/UF + H5xfesEM+k9TySEFbxsmhAeXsrZFLDlBAiZhqjOaRsgzKuBribhC2D5eEY0LZKADj4ThsRkm0AL7 + GV5m1ESuIOkECxSEJKe3/ZVwjpiAC08tieZpAsH/NVwN/wHQiK4AUwYAAA== + headers: + content-encoding: [gzip] + content-length: ['499'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: null + headers: {} + method: GET + uri: https://api.balancedpayments.com:443/customers/CU3O2IKYocPZ5LA7GkZWpwKZ + response: + body: + string: !!binary | + H4sIAAAAAAAAA41UTY/TMBS876+IcqabpJu2orcVB4QWCQ4gRBGKXNsoVhM7+AOoqvx3ntMk69oB + 7yWH55mJ37zxu9wlSYqN0qKlUqX75BsUkuQyfOGIo5ZClZumeTXVGsZPFjqBAKaEkdgHQp1QpRlH + mgk+qowi/axmOoI0JRXSgEjXeVGu8t0qLz4Vm335sC+397tNmW/zQzpTsKQRynazLnavHQoRx6oV + XNd+M4zY3775/PBh/e7pq8AfD5v3j7u3p8OX7veTI9DVgvsNprWkPyw9mx3M4kpgNK4R15XSSBtr + ZMrFqkUa188ttlQjz2L6czAoDRy0vZ0pkn5riBBJlTcpzPTZA8KcYKR0vVwuwrK9uO8FiHQCDpoK + C7JwiIXhWp7d06CRo1FwD6WqpdQpxasGKV36bdIWsSYospvA9fCv7zY/tlMnvM/Rv58jnGbg2pBn + lV3mwY7n/Tgih4iRJFUtGjKM0omCQ2akzxzcNcmOxhHxU4XwYFJE5hYaKBF6ZDGJEbPAdV/rP3xw + nvSCGfSPppJDCl7WTAgPLmVti1hyhQRMwlRnNI2QZ1TA1xJxhbBdXhGNG2SgA0vC8FgPE2iB/Qs2 + M2oiV5B0ggUKQpLrbv9POEdMwIVVS6J5mkDwvvq7/i9xeE/EUwYAAA== + headers: + content-encoding: [gzip] + content-length: ['496'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: null + headers: {} + method: GET + uri: https://api.balancedpayments.com:443/customers/CU3O2IKYocPZ5LA7GkZWpwKZ/credits?limit=10&offset=0 + response: + body: + string: !!binary | + H4sIAAAAAAAAA61UXW/TMBR936+o/MATXdLGbddKEyoDVaiTKGPTWBGKjHNDzBI7s52xqcp/x3bj + JCuqeICXKLkf55z7ld3JYICohIRphRaDr+ZzMNi5p3EoTXRl7UhVlAIkkKDX3pmAopKVmgluIzJR + gOy8OeP3NtNjWZpKaRdkoi9uoo/jD+s7QTfbyeVytrrf3pa/1tsWwMQbAs048QRvl9GnS67er35I + 9fMhzb5kONo84E6RSREycfi8yvNGZ90KrsqEaEhioq2CcTjCw3A2DEfXo8kC4wWen86iaTiJOhG2 + M39JwdOzCM96KVoSrgi1qmNeFd+beq/C+XQY4Wg4HYdR16aUsLySEBse5fpolbeKaSUlcPps9d58 + ftelkUJU3JYxmoRhG/4SLKYigUPEAjSxU+m6kklILX7QbEFwcYVvV+lzJpbr+Srf3p3hyTXZ0KzH + XpZApIpNhXZDoACuY+L2BJ5IUeZwSkXRxbPEuo7iukHV5vnNVoK8RGdGOVFuXIHfHhUc2x1fwZuc + FUyfj8JXIk0V6POwkYI4PFmwrseoq75Zzn+Bd7x2KM1I0J7fWLyhlPDIhDupnggtNMlt3n6SKGXy + fxRtGujGjF7eoj/3U38sKHBvKtg1K7D31L5r3mq0myiSu0G369ImsaQOupCmFJ/bv/3eKNtk7/+D + 1HBy92s6wtj4D+gOfh1GlxKVpNCrsRdSI9uqk/o3GBlRDw0FAAA= + headers: + content-encoding: [gzip] + content-length: ['551'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: null + headers: {} + method: GET + uri: https://api.balancedpayments.com:443/customers/CU3O2IKYocPZ5LA7GkZWpwKZ/credits?limit=10&offset=0 + response: + body: + string: !!binary | + H4sIAAAAAAAAA61UXW/TMBR936+o/MATXdLGbddKEyoDVaiTKGPTWBGKjHNDzBI7s52xqcp/x3bj + JCuqeICXKLkf55z7ld3JYICohIRphRaDr+ZzMNi5p3EoTXRl7UhVlAIkkKDX3pmAopKVmgluIzJR + gOy8OeP3NtNjWZpKaRdkoi9uoo/jD+s7QTfbyeVytrrf3pa/1tsWwMQbAs048QRvl9GnS67er35I + 9fMhzb5kONo84E6RSREycfi8yvNGZ90KrsqEaEhioq2CcTjCw3A2DEfXo8kC4wWen86iaTiJOhG2 + M39JwdOzCM96KVoSrgi1qmNeFd+beq/C+XQY4Wg4HYdR16aUsLySEBse5fpolbeKaSUlcPps9d58 + ftelkUJU3JYxmoRhG/4SLKYigUPEAjSxU+m6kklILX7QbEFwcYVvV+lzJpbr+Srf3p3hyTXZ0KzH + XpZApIpNhXZDoACuY+L2BJ5IUeZwSkXRxbPEuo7iukHV5vnNVoK8RGdGOVFuXIHfHhUc2x1fwZuc + FUyfj8JXIk0V6POwkYI4PFmwrseoq75Zzn+Bd7x2KM1I0J7fWLyhlPDIhDupnggtNMlt3n6SKGXy + fxRtGujGjF7eoj/3U38sKHBvKtg1K7D31L5r3mq0myiSu0G369ImsaQOupCmFJ/bv/3eKNtk7/+D + 1HBy92s6wtj4D+gOfh1GlxKVpNCrsRdSI9uqk/o3GBlRDw0FAAA= + headers: + content-encoding: [gzip] + content-length: ['551'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: null + headers: {} + method: GET + uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd/debits?limit=10&offset=0 + response: + body: + string: !!binary | + H4sIAAAAAAAAA+1VyW7bMBC95ysCH3ooKpkSRVkKEBReChiooxTZ3AWFwYh0o0RbScqIG/jfS1GU + JQtxm6I+5mKIM5w3M28ex09Hx8e9hArcOzl+kt/yFGMu5KnXDwsusoQy3h9fw4+fyXoVXE7oevYJ + 5sM58+9+kj6ht5Hg7+MoicSpBd5kyyWn4hT03lVYKX0ssdIijrXljtHlodBVWglmAQ1epZeW2pAz + uoqygu/WIDKBY2mCOmwZsQO0LDvelIC9OEofyoyaz4ojs2az0/xTx72pqdN2EvG8EFRF6W/er4O0 + oRvDs4KFVQij1aGJqc7dkIwRylQS9dVcV8fubTnDIiVlj7IoJYFtSRHZ9Gu3pld3Qlc0FXtjtHdL + YhUkM3xTSqq4lORygYWaZ48XYUgpoUQzJp2E8pBFuYiytLxxj1MqGu/uXJTW20PZJ/ItQJl9y+x4 + DM+mIz53V1M8g7yY3ZCrm9tzv327JrWRv0rajLR0qPa0dJS7yAkWlCyweoU2sBwDDAxgXVnoxHFO + HM8EwLds72vTWcjoH0MGJoQIItAKEQynHIclVYu0SG6r6c+R5xvAsQ1kWV6Dv8RRXDC6kGm4orbd + kSSRMZqG67Lc68tJE4aTrEjVA0UDtxKD7G8XbBFmpJTqDmK9kNR7UqRs14aW23ziXN8FjKQum6Mk + 80ceWcMb3NICznOKGV/I/krN0ESqb4GVckbD2Vv6iJM8pmaYJU3BESnde7FVMbqmV0FuBYlM5HjI + tf5BkI7p2y602hreI0jbBQZ0PMNCNjygIAHQfxEHFOTF0FmHV+Gv/IKcAQqDafAlQq3X8D+CfB77 + VZDPbUgITRtBC9gvFyS0TeQBCPy/b0jXAcZgAAzHc5pl31lq3X32gg0JXOvgG3Iy9YPx/Qc2igIE + HmP7Bxg+BAcS5PPYlSDl7/ejzW/H5EyAWwoAAA== + headers: + content-encoding: [gzip] + content-length: ['712'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: null + headers: {} + method: GET + uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd/debits?limit=10&offset=0 + response: + body: + string: !!binary | + H4sIAAAAAAAAA+1VyW7bMBC95ysCH3ooKpkSRVkKEBReChiooxTZ3AWFwYh0o0RbScqIG/jfS1GU + JQtxm6I+5mKIM5w3M28ex09Hx8e9hArcOzl+kt/yFGMu5KnXDwsusoQy3h9fw4+fyXoVXE7oevYJ + 5sM58+9+kj6ht5Hg7+MoicSpBd5kyyWn4hT03lVYKX0ssdIijrXljtHlodBVWglmAQ1epZeW2pAz + uoqygu/WIDKBY2mCOmwZsQO0LDvelIC9OEofyoyaz4ojs2az0/xTx72pqdN2EvG8EFRF6W/er4O0 + oRvDs4KFVQij1aGJqc7dkIwRylQS9dVcV8fubTnDIiVlj7IoJYFtSRHZ9Gu3pld3Qlc0FXtjtHdL + YhUkM3xTSqq4lORygYWaZ48XYUgpoUQzJp2E8pBFuYiytLxxj1MqGu/uXJTW20PZJ/ItQJl9y+x4 + DM+mIz53V1M8g7yY3ZCrm9tzv327JrWRv0rajLR0qPa0dJS7yAkWlCyweoU2sBwDDAxgXVnoxHFO + HM8EwLds72vTWcjoH0MGJoQIItAKEQynHIclVYu0SG6r6c+R5xvAsQ1kWV6Dv8RRXDC6kGm4orbd + kSSRMZqG67Lc68tJE4aTrEjVA0UDtxKD7G8XbBFmpJTqDmK9kNR7UqRs14aW23ziXN8FjKQum6Mk + 80ceWcMb3NICznOKGV/I/krN0ESqb4GVckbD2Vv6iJM8pmaYJU3BESnde7FVMbqmV0FuBYlM5HjI + tf5BkI7p2y602hreI0jbBQZ0PMNCNjygIAHQfxEHFOTF0FmHV+Gv/IKcAQqDafAlQq3X8D+CfB77 + VZDPbUgITRtBC9gvFyS0TeQBCPy/b0jXAcZgAAzHc5pl31lq3X32gg0JXOvgG3Iy9YPx/Qc2igIE + HmP7Bxg+BAcS5PPYlSDl7/ejzW/H5EyAWwoAAA== + headers: + content-encoding: [gzip] + content-length: ['712'] + content-type: [application/json] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/py/fixtures/TestPaydayCharge.yml b/tests/py/fixtures/TestPaydayCharge.yml deleted file mode 100644 index 2bae9a87d8..0000000000 --- a/tests/py/fixtures/TestPaydayCharge.yml +++ /dev/null @@ -1,280 +0,0 @@ -interactions: -- request: - body: null - headers: {} - method: GET - uri: https://api.balancedpayments.com:443/customers/CU2RCigJCqQDdYQEcThv3Jxr - response: - body: - string: !!binary | - H4sIAAAAAAAAA41Uy27bMBC85ysEnevIkp028NXtJbcU6aEtCmFNshURiXT4CGIY+vcubUmmSaXs - RYfdmSF3dsTjTZblxGojO6Z0vsl+YiHLjqcvtgR0DKvCtu2HsdZy8eygIwhhWlpFQiDWKdOGCzBc - ikFlEOknNbunYBitwSAir5blerH8tFiWT2W5WVWbqrpd35X398sf+UQhiiUoq2p993HlUajc1Z0U - pgmH4dQdu/1Wfd3yPw/bl8fP9PvjF/LUvK4e3tTlzH0jRThg3ij229GLycEirYRGkwaEqbUBY52R - uZCLDgxpLsd1zEBgMXtx0DKPHHSzHRiocDSgVDEdbIpwcwiAuCdcKavmy2VcdhcPvUCRvcRGWxNJ - Z5pEWmHUwe9Gg+ysxntoXc+lTmtRt6DNOhyTdcDbqMivAtfjWb9cftykXngv0b+dIpwX6Nopz7o4 - Tosd+v2wIo9IQNG6kS09rdKLgkfmtC883DnJnsYOxHMN5GRSQuYaGilRtuMpiQEzw/X/1nd88H7p - GTPYm2FKYAr+b5gYHl3K2Zaw5AyJmJTrvTUsQZ5QEd8oEBqIe7wSGlfISAcfCStSM4ygGfYrvszQ - Jq6g2AiLFKSi57f9H+EcMBEXn1qazNMIwv+rv+n/AoxtLhJTBgAA - headers: - content-encoding: [gzip] - content-length: ['495'] - content-type: [application/json] - status: {code: 200, message: OK} -- request: - body: null - headers: {} - method: GET - uri: https://api.balancedpayments.com:443/customers/CU2RCigJCqQDdYQEcThv3Jxr/cards?limit=10&offset=0 - response: - body: - string: !!binary | - H4sIAAAAAAAAA6VVyW7bMBC95ysIHXpKbIlyvAFB4ThBigRx9gJNUQg0F5uITDok5dgI/O8lKcmS - sqBFy4MAzcKZefNm+LoHQICRIjoYgp/2B4BX/7XilIsnJy4FzjLTRi6ostJg/ABvx3x2Pn6+OSE/ - bk7x/XwVn69VUPhv98uLGBczqpaKC+Mc+7jHwg6M4+4hYv0+jmncxSyEvU6XYtTtMIJZ3CUEQxqx - eGDNKR6wmDESHiLY7QW7i/FqlSiqs9TdK7I03WlEtpjmaa5rJ7Kn8kYrnSylNihNFsjguctNyEpP - 10uukOFSJAspjNNH4S7CghrUBEfRmbX10EjBMu1+SnOHHTebxMgXb1FXIEJsETqBTnEs1wC+h5AT - f+8Y3p1dn0yujx9CeXUxRVQPzPO4VhNGhs6k2jhraeYWgl3CZrOkTowVJdw0QSzrb2A4ReIpEWjh - vc6vL69uz0YTMP42ujsFx6PJxT6YtEatJp7aKEpNheeG6spgqpDwdXznGlXibEls1iRBnh0wjDoH - Ye8gjO6jaBjDIYxb/X4fdgaPtVA5ZNa+Tk4LsJXUa8hZTB2yH4gjFy+CMbhEXIA7n3q9MZYaxhf/ - YT8L6mBJvM0ghFFY98YyE0ZtksLAxX83GRiJhNCpbccQGJXRir857I1+1Pi4ocjNIAxhRUg7DG/K - DLhOVlRxxqmDvRHAkX83OkFeOyiYCGybQF4ecNkDIoGQBniW1Brusi/YNAQMpbrKf64oc6i0/W5p - /wVxFf0DCWC/04l7j/lsbC2UvxxaQWMOgxRpz6J2uals7E/2VJ7a15QvuDmKwi+SMU3NUdnDQNB1 - c60EVVHFGvz3y31Um2i5UII8upUUDQ2Wiq64zNwCrlgQGGnXlfPLmRIwrv6/YAujX9ZvNn7+LrQI - 18vM2Dmu2vnqu9riZNveaYuEck39nai1ovArtdtiXIpAzjWZy9S/RSVzaqFq+kYwP0Cf+BQ6V+He - 9jfXVB+z7QYAAA== - headers: - content-encoding: [gzip] - content-length: ['751'] - content-type: [application/json] - status: {code: 200, message: OK} -- request: - body: '{"amount": 1576, "description": "janet"}' - headers: {} - method: POST - uri: https://api.balancedpayments.com:443/cards/CC2SGPDNPBU0oOKbaes9tqC1/debits - response: - body: {string: !!python/unicode "{\n \"debits\": [\n {\n \"status\": - \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"customer\": - \"CU2RCigJCqQDdYQEcThv3Jxr\",\n \"source\": \"CC2SGPDNPBU0oOKbaes9tqC1\",\n - \ \"order\": null,\n \"dispute\": null\n },\n \"updated_at\": - \"2014-07-01T11:32:58.045082Z\",\n \"created_at\": \"2014-07-01T11:32:57.356510Z\",\n - \ \"transaction_number\": \"W081-983-3951\",\n \"failure_reason\": - null,\n \"currency\": \"USD\",\n \"amount\": 1576,\n \"failure_reason_code\": - null,\n \"meta\": {},\n \"href\": \"/debits/WD3v0UIBWb9TGirT5zELhtlj\",\n - \ \"appears_on_statement_as\": \"BAL*example.com\",\n \"id\": \"WD3v0UIBWb9TGirT5zELhtlj\"\n - \ }\n ],\n \"links\": {\n \"debits.customer\": \"/customers/{debits.customer}\",\n - \ \"debits.dispute\": \"/disputes/{debits.dispute}\",\n \"debits.source\": - \"/resources/{debits.source}\",\n \"debits.order\": \"/orders/{debits.order}\",\n - \ \"debits.refunds\": \"/debits/{debits.id}/refunds\",\n \"debits.events\": - \"/debits/{debits.id}/events\"\n }\n}"} - headers: - content-length: ['1002'] - content-type: [application/json] - status: {code: 201, message: CREATED} -- request: - body: null - headers: {} - method: GET - uri: https://api.balancedpayments.com:443/customers/CU2Uomy8gvhh6SdpAqQO2wHv - response: - body: - string: !!binary | - H4sIAAAAAAAAA41Uy27bMBC85ysEnevIUoxY9S3opbeiaHJpUQg0yUREJFLhw6lh6N+7lCWZJtWw - Fx12Z4bc2RFPN0mSYqO0aKlU6S75BYUkOQ1faHHUUqhy0zSfplrD+KuFTiCAKWEk9oFQJ1RpxpFm - go8qo0g/q5mOIE1JhTQg0mKdb1br7WqdP+b57q7YFZvbsvy83RY/05mCJY1QtuXd5r50KETsq1Zw - XfvDMGKP/fJUPIn2WL4c6vr+B+ke3r5/K96/Hi5ndrXg/oBpLemzpWezg1lcCYzGNeK6UhppY41M - uVi1SOP6clxLNfIspm+DQWngoJ3tSJH0R0OESKq8TWGmjx4Q9gQrpcVyOQ/L9uK+FyDSCWg0FRZk - oYmF4Voe3W4wyN4ouIdS1VLqlOJVg5Te+GPSFrEmKLKrwPVw1m+bHzupE95L9G/nCKcZuDbkWWWn - ebFjvx9X5BAxkqSqRUOGVTpRcMiM9JmDOyfZ0dgj/lohPJgUkbmGBkqE7llMYsQscN2/9R8+OL/0 - ghn0j6aSQwr+b5gQHlzK2hax5AwJmISpzmgaIc+ogK8l4gph+3hFNK6QgQ48EobHZphAC+wDvMyo - iVxB0gkWKAhJzm/7B+EcMQEXnloSzdMEgv+rv+n/AqhHO/tTBgAA - headers: - content-encoding: [gzip] - content-length: ['495'] - content-type: [application/json] - status: {code: 200, message: OK} -- request: - body: null - headers: {} - method: GET - uri: https://api.balancedpayments.com:443/customers/CU2Uomy8gvhh6SdpAqQO2wHv/bank_accounts?limit=10&offset=0 - response: - body: - string: !!binary | - H4sIAAAAAAAAA61UXW+bMBR9769APOxpTbADJESqpmTqVm1do63rpnWakGObxApfNSYLivjvsx0I - kIapD31B4t7re8895+juLwzDXKJ44yOMkzwWmTk1fsugYez1V6Z5kgsWr/w4j5aUy7wJILBsOJlA - 821dpHvEKKIq//P69vbe+DD79nFhzGd3n427WVNZDfJFkepivKZ4I/s3FXWbmySi3PiEiiYVsnij - INbgJDycZ0IVql7vH+BDEhWT1Xa9du9JOnv6uoB/b7bHBifb+lvKWcAwEiyJZYM4D8Nqo/K4Gkax - jzklTMgKwXPaZDhFghIfqYwJLWBfWuNLC3wHYDqCU+gMHNe2vcljs0AgN6U85SzWbwjCrgNs4hEy - QgCTMcIedIgztj1qjawJhjIfIDjGjuUiGiwt7C0hcAEIAs9zvaZxnpL/YnEH0PUcx25hWXMaKBDD - jgGG8xn8sRrz+cr9wh+jBXd3xdP1L0yaWREVSInQcFSL2nhkt9vB0Qi1dCeE0+xUPCaKiveaVSmR - VJnC82HwPJwJufjzcJrIROjjhJxJaq/zop09qzuhSy17gMKs0Z0RRVsvT7pTKb9/1E5mzZYOmyHK - tPLD2rfZsM+1XV3ehSxi4gpYb5IgyKi4sipuzZjuVEtl3gOJ5lHZ1xuip8spwKpmHFDISB1IOd2y - JFcKt6CIRKqg3lXPAsZfjwBJqfagckzrLnQv2kCLqGCdOn3fMf6AkXJY1VZgu/n2X+dyvLB3+9hk - tXjdEe1j1nLICdC6quxpoo/VC0EdLlsPnN6NNZmdfYYnGHuflqZS7aL8B/8D0Fl7BgAA - headers: - content-encoding: [gzip] - content-length: ['621'] - content-type: [application/json] - status: {code: 200, message: OK} -- request: - body: '{"amount": 1500.00, "description": "homer"}' - headers: {} - method: POST - uri: https://api.balancedpayments.com:443/bank_accounts/BA2Vg7rBg6MrZmOr6xyqEYcd/credits - response: - body: {string: !!python/unicode "{\n \"credits\": [\n {\n \"status\": - \"succeeded\",\n \"description\": \"homer\",\n \"links\": {\n \"customer\": - \"CU2Uomy8gvhh6SdpAqQO2wHv\",\n \"destination\": \"BA2Vg7rBg6MrZmOr6xyqEYcd\",\n - \ \"order\": null\n },\n \"updated_at\": \"2014-07-01T11:32:59.459492Z\",\n - \ \"created_at\": \"2014-07-01T11:32:59.196481Z\",\n \"transaction_number\": - \"CR642-211-6199\",\n \"failure_reason\": null,\n \"currency\": - \"USD\",\n \"amount\": 1500,\n \"failure_reason_code\": null,\n - \ \"meta\": {},\n \"href\": \"/credits/CR3x5EJmWYUuxmxNIzyYmDkl\",\n - \ \"appears_on_statement_as\": \"example.com\",\n \"id\": \"CR3x5EJmWYUuxmxNIzyYmDkl\"\n - \ }\n ],\n \"links\": {\n \"credits.order\": \"/orders/{credits.order}\",\n - \ \"credits.customer\": \"/customers/{credits.customer}\",\n \"credits.destination\": - \"/resources/{credits.destination}\",\n \"credits.reversals\": \"/credits/{credits.id}/reversals\",\n - \ \"credits.events\": \"/credits/{credits.id}/events\"\n }\n}"} - headers: - content-length: ['955'] - content-type: [application/json] - status: {code: 201, message: CREATED} -- request: - body: null - headers: {} - method: GET - uri: https://api.balancedpayments.com:443/customers/CU2RCigJCqQDdYQEcThv3Jxr - response: - body: - string: !!binary | - H4sIAAAAAAAAA41Uy27bMBC85ysEnevIkp028NXtJbcU6aEtCmFNshURiXT4CGIY+vcubUmmSaXs - RYfdmSF3dsTjTZblxGojO6Z0vsl+YiHLjqcvtgR0DKvCtu2HsdZy8eygIwhhWlpFQiDWKdOGCzBc - ikFlEOknNbunYBitwSAir5blerH8tFiWT2W5WVWbqrpd35X398sf+UQhiiUoq2p993HlUajc1Z0U - pgmH4dQdu/1Wfd3yPw/bl8fP9PvjF/LUvK4e3tTlzH0jRThg3ij229GLycEirYRGkwaEqbUBY52R - uZCLDgxpLsd1zEBgMXtx0DKPHHSzHRiocDSgVDEdbIpwcwiAuCdcKavmy2VcdhcPvUCRvcRGWxNJ - Z5pEWmHUwe9Gg+ysxntoXc+lTmtRt6DNOhyTdcDbqMivAtfjWb9cftykXngv0b+dIpwX6Nopz7o4 - Tosd+v2wIo9IQNG6kS09rdKLgkfmtC883DnJnsYOxHMN5GRSQuYaGilRtuMpiQEzw/X/1nd88H7p - GTPYm2FKYAr+b5gYHl3K2Zaw5AyJmJTrvTUsQZ5QEd8oEBqIe7wSGlfISAcfCStSM4ygGfYrvszQ - Jq6g2AiLFKSi57f9H+EcMBEXn1qazNMIwv+rv+n/AoxtLhJTBgAA - headers: - content-encoding: [gzip] - content-length: ['495'] - content-type: [application/json] - status: {code: 200, message: OK} -- request: - body: null - headers: {} - method: GET - uri: https://api.balancedpayments.com:443/customers/CU2Uomy8gvhh6SdpAqQO2wHv - response: - body: - string: !!binary | - H4sIAAAAAAAAA41Uy27bMBC85ysEnevIUoxY9S3opbeiaHJpUQg0yUREJFLhw6lh6N+7lCWZJtWw - Fx12Z4bc2RFPN0mSYqO0aKlU6S75BYUkOQ1faHHUUqhy0zSfplrD+KuFTiCAKWEk9oFQJ1RpxpFm - go8qo0g/q5mOIE1JhTQg0mKdb1br7WqdP+b57q7YFZvbsvy83RY/05mCJY1QtuXd5r50KETsq1Zw - XfvDMGKP/fJUPIn2WL4c6vr+B+ke3r5/K96/Hi5ndrXg/oBpLemzpWezg1lcCYzGNeK6UhppY41M - uVi1SOP6clxLNfIspm+DQWngoJ3tSJH0R0OESKq8TWGmjx4Q9gQrpcVyOQ/L9uK+FyDSCWg0FRZk - oYmF4Voe3W4wyN4ouIdS1VLqlOJVg5Te+GPSFrEmKLKrwPVw1m+bHzupE95L9G/nCKcZuDbkWWWn - ebFjvx9X5BAxkqSqRUOGVTpRcMiM9JmDOyfZ0dgj/lohPJgUkbmGBkqE7llMYsQscN2/9R8+OL/0 - ghn0j6aSQwr+b5gQHlzK2hax5AwJmISpzmgaIc+ogK8l4gph+3hFNK6QgQ48EobHZphAC+wDvMyo - iVxB0gkWKAhJzm/7B+EcMQEXnloSzdMEgv+rv+n/AqhHO/tTBgAA - headers: - content-encoding: [gzip] - content-length: ['495'] - content-type: [application/json] - status: {code: 200, message: OK} -- request: - body: null - headers: {} - method: GET - uri: https://api.balancedpayments.com:443/customers/CU2Uomy8gvhh6SdpAqQO2wHv/credits?limit=10&offset=0 - response: - body: - string: !!binary | - H4sIAAAAAAAAA61UW2/TMBh936+I/MATbeKszZZKE9pNAiSY2CioQygyjttai53Ml5JS5b9ju3GS - FVU8wEuUfLdzznfJ7iQIABYkp0qCWfDNfAbBzj2NQyqktLUDqTEmJCc5eO2dOZFY0ErRktuIdcmI - 6L0F5U8209eyMFoqF2Sir+fxvGTb89VmvU4e8ury+dNd/PPtpitg4g2Aohx5gKvL+MvqTFytkg/i - kd2JpN4+3y5wz8iklCJ39bkuipZn0xHWVY4UyTOkLIM4gpNRdDaK4GcIZ6fxbJqOJ9N0ksaPvQrT - mb+kwDSZnMNBihKIS4Qt64xr9qPVe59M4lEM4SiBadoDLBEttCCZwZGuj5Z5xxhrIQjHW8t3/nDT - pyFWam5lwGkUdeEvi2W4zMlhRUYUslPpu7IWZGnrh+0WhNf3p/X09j37upjrmtUf3/3aLtjNUzFA - ryqChMyMQrshhBGuMuT2hNSIVQUZ45L18TS3rqN13aAa8/xulQBP0ZlBgaQbV+i3R4bHdscreFNQ - RtUFjF6Vy6Uk6iJqqQBOalus7zHo1bfL+S/lHa4dSjsSsMc3Fm+oBNnQ0p3UgIQqFSps3n6SYEnF - /xBtGujGDF7eoj/3sT8WELo3Ge7aFdh7Gt81bzXcTRQq3KC7demSaN6EfUgrxecOb38wyi7Z+/8A - NZjc/ZqOILb+A7iDX4fhJUstMBloHIQ0wLbqpPkNYgZkFQ0FAAA= - headers: - content-encoding: [gzip] - content-length: ['551'] - content-type: [application/json] - status: {code: 200, message: OK} -- request: - body: null - headers: {} - method: GET - uri: https://api.balancedpayments.com:443/customers/CU2Uomy8gvhh6SdpAqQO2wHv/credits?limit=10&offset=0 - response: - body: - string: !!binary | - H4sIAAAAAAAAA61UW2/TMBh936+I/MATbeKszZZKE9pNAiSY2CioQygyjttai53Ml5JS5b9ju3GS - FVU8wEuUfLdzznfJ7iQIABYkp0qCWfDNfAbBzj2NQyqktLUDqTEmJCc5eO2dOZFY0ErRktuIdcmI - 6L0F5U8209eyMFoqF2Sir+fxvGTb89VmvU4e8ury+dNd/PPtpitg4g2Aohx5gKvL+MvqTFytkg/i - kd2JpN4+3y5wz8iklCJ39bkuipZn0xHWVY4UyTOkLIM4gpNRdDaK4GcIZ6fxbJqOJ9N0ksaPvQrT - mb+kwDSZnMNBihKIS4Qt64xr9qPVe59M4lEM4SiBadoDLBEttCCZwZGuj5Z5xxhrIQjHW8t3/nDT - pyFWam5lwGkUdeEvi2W4zMlhRUYUslPpu7IWZGnrh+0WhNf3p/X09j37upjrmtUf3/3aLtjNUzFA - ryqChMyMQrshhBGuMuT2hNSIVQUZ45L18TS3rqN13aAa8/xulQBP0ZlBgaQbV+i3R4bHdscreFNQ - RtUFjF6Vy6Uk6iJqqQBOalus7zHo1bfL+S/lHa4dSjsSsMc3Fm+oBNnQ0p3UgIQqFSps3n6SYEnF - /xBtGujGDF7eoj/3sT8WELo3Ge7aFdh7Gt81bzXcTRQq3KC7demSaN6EfUgrxecOb38wyi7Z+/8A - NZjc/ZqOILb+A7iDX4fhJUstMBloHIQ0wLbqpPkNYgZkFQ0FAAA= - headers: - content-encoding: [gzip] - content-length: ['551'] - content-type: [application/json] - status: {code: 200, message: OK} -- request: - body: null - headers: {} - method: GET - uri: https://api.balancedpayments.com:443/customers/CU2RCigJCqQDdYQEcThv3Jxr/debits?limit=10&offset=0 - response: - body: - string: !!binary | - H4sIAAAAAAAAA61UXW/aMBR9769AedjDNIhDmvIhVdMIVbWu2toVhLZpikx8Ge4SO7UdxIby32c7 - DgEk9tSXyPbNueeec6+9u+h0vBwU9sadnV7rXYal0jvPT0upeA5C+vG8/zWmv+7il8cp+fZ4k87W - m/BuK3wCS6rk+4zmVF0H6A1frSSoa+S9q3Mx2JpcrMwyd7IWsHqt7JZWJwuQS17T65PmoBCwobyU - xzUornBmcA62ouIVJGvFlUnoZZT9NozOz9qjXuPmifjdSbhqrHPnhMqiVGBRbi39BuQOTjGwAaZM - BRpiG7QHUFL5Luq0OxouCAiLsKsWYbenBLqHJSNnGZrwMYXkpUhrIQLqTUtT7ytvb2Jdl67oh52k - 2kttrlRY2X56skxTAALEOaaDBGQqaKEoZ+aPZ8xAtdHjvthZP2zKuSHfJzDsew1x3H+6fZh+fpjM - Ef/yaYlBjtRLHBz+3Zjajr8lbVtqAlaeGx0bLguCFZAE21vYR8FlFw26KJgFwTjsj6NhD11GaNj/ - 3ipLBfwXMuiF0VUUoAOIEphJnBqrElbmy7r7CzQMuqNh2A1HUSvFW2GalQISTSOttYeKtIlCAEv/ - mHLnT9O2LJzzktkLGg2u6mHQ+o6TJSknZiiOMjYPkr1P1pT9s+EGejENN2j+cbJYjma3VMyivzf3 - a5U9H5AXBWAhE63PzAzk+k4k2E7O5MP9W9jivMigl/K8xVBiwmdz22Iq/f15Uf0DV/ylBTkFAAA= - headers: - content-encoding: [gzip] - content-length: ['569'] - content-type: [application/json] - status: {code: 200, message: OK} -- request: - body: null - headers: {} - method: GET - uri: https://api.balancedpayments.com:443/customers/CU2RCigJCqQDdYQEcThv3Jxr/debits?limit=10&offset=0 - response: - body: - string: !!binary | - H4sIAAAAAAAAA61UXW/aMBR9769AedjDNIhDmvIhVdMIVbWu2toVhLZpikx8Ge4SO7UdxIby32c7 - DgEk9tSXyPbNueeec6+9u+h0vBwU9sadnV7rXYal0jvPT0upeA5C+vG8/zWmv+7il8cp+fZ4k87W - m/BuK3wCS6rk+4zmVF0H6A1frSSoa+S9q3Mx2JpcrMwyd7IWsHqt7JZWJwuQS17T65PmoBCwobyU - xzUornBmcA62ouIVJGvFlUnoZZT9NozOz9qjXuPmifjdSbhqrHPnhMqiVGBRbi39BuQOTjGwAaZM - BRpiG7QHUFL5Luq0OxouCAiLsKsWYbenBLqHJSNnGZrwMYXkpUhrIQLqTUtT7ytvb2Jdl67oh52k - 2kttrlRY2X56skxTAALEOaaDBGQqaKEoZ+aPZ8xAtdHjvthZP2zKuSHfJzDsew1x3H+6fZh+fpjM - Ef/yaYlBjtRLHBz+3Zjajr8lbVtqAlaeGx0bLguCFZAE21vYR8FlFw26KJgFwTjsj6NhD11GaNj/ - 3ipLBfwXMuiF0VUUoAOIEphJnBqrElbmy7r7CzQMuqNh2A1HUSvFW2GalQISTSOttYeKtIlCAEv/ - mHLnT9O2LJzzktkLGg2u6mHQ+o6TJSknZiiOMjYPkr1P1pT9s+EGejENN2j+cbJYjma3VMyivzf3 - a5U9H5AXBWAhE63PzAzk+k4k2E7O5MP9W9jivMigl/K8xVBiwmdz22Iq/f15Uf0DV/ylBTkFAAA= - headers: - content-encoding: [gzip] - content-length: ['569'] - content-type: [application/json] - status: {code: 200, message: OK} -version: 1 From 98d5ed77d6fdea6378d38e951ec23d2ce8aef1ab Mon Sep 17 00:00:00 2001 From: Changaco Date: Sun, 22 Jun 2014 14:25:33 +0200 Subject: [PATCH 12/97] modify Payday.start() to be a class method --- gittip/billing/payday.py | 58 ++++++++++++++++++++-------------------- gittip/cli.py | 2 +- gittip/wireup.py | 2 ++ 3 files changed, 32 insertions(+), 30 deletions(-) diff --git a/gittip/billing/payday.py b/gittip/billing/payday.py index 14fea554c2..637e6fdc4d 100644 --- a/gittip/billing/payday.py +++ b/gittip/billing/payday.py @@ -43,11 +43,6 @@ class Payday(object): """ - def __init__(self, db): - """Takes a postgres.Postgres instance. - """ - self.db = db - def run(self): """This is the starting point for payday. @@ -61,16 +56,16 @@ def run(self): _start = aspen.utils.utcnow() log("Greetings, program! It's PAYDAY!!!!") - ts_start = self.start() - self.prepare(ts_start) - self.zero_out_pending(ts_start) + + self.prepare() + self.zero_out_pending() self.payin() self.move_pending_to_balance_for_teams() self.pachinko() self.clear_pending_to_balance() self.payout() - self.update_stats(ts_start) + self.update_stats() self.update_receiving_amounts() self.end() @@ -83,7 +78,8 @@ def run(self): log(aspen.utils.to_age(_start, fmt_past=fmt_past)) - def start(self): + @classmethod + def start(cls): """Try to start a new Payday. If there is a Payday that hasn't finished yet, then the UNIQUE @@ -93,24 +89,29 @@ def start(self): """ try: - ts_start = self.db.one("INSERT INTO paydays DEFAULT VALUES " - "RETURNING ts_start") + d = cls.db.one(""" + INSERT INTO paydays DEFAULT VALUES + RETURNING id, (ts_start AT TIME ZONE 'UTC') AS ts_start + """, back_as=dict) log("Starting a new payday.") except IntegrityError: # Collision, we have a Payday already. - ts_start = self.db.one(""" - - SELECT ts_start + d = cls.db.one(""" + SELECT id, (ts_start AT TIME ZONE 'UTC') AS ts_start FROM paydays WHERE ts_end='1970-01-01T00:00:00+00'::timestamptz - - """) + """, back_as=dict) log("Picking up with an existing payday.") - log("Payday started at %s." % ts_start) - return ts_start + d['ts_start'] = d['ts_start'].replace(tzinfo=aspen.utils.utc) + + log("Payday started at %s." % d['ts_start']) + payday = Payday() + payday.__dict__.update(d) + return payday - def prepare(self, ts_start): + + def prepare(self): """Prepare the DB: we need temporary tables with indexes. """ self.db.run(""" @@ -188,11 +189,11 @@ def prepare(self, ts_start): CREATE INDEX ON pay_takes (team); - """, dict(ts_start=ts_start)) + """, dict(ts_start=self.ts_start)) log('Prepared the DB.') - def zero_out_pending(self, ts_start): + def zero_out_pending(self): """Given a timestamp, zero out the pending column. We keep track of balance changes as a result of Payday in the pending @@ -208,7 +209,7 @@ def zero_out_pending(self, ts_start): AND claimed_time < %s """ - self.db.run(START_PENDING, (ts_start,)) + self.db.run(START_PENDING, (self.ts_start,)) log("Zeroed out the pending column.") return None @@ -375,7 +376,7 @@ def clear_pending_to_balance(self): log("Cleared pending to balance. Ready for payouts.") - def update_stats(self, ts_start): + def update_stats(self): self.db.run("""\ WITH our_transfers AS ( @@ -434,7 +435,7 @@ def update_stats(self, ts_start): , charge_fees_volume = (SELECT sum(fee) FROM our_charges) WHERE ts_end='1970-01-01T00:00:00+00'::timestamptz - """, {'ts_start': ts_start}) + """, {'ts_start': self.ts_start}) def update_receiving_amounts(self): @@ -458,15 +459,14 @@ def update_receiving_amounts(self): cursor.execute(UPDATE) def end(self): - self.db.one("""\ + self.ts_end = self.db.one("""\ UPDATE paydays SET ts_end=now() WHERE ts_end='1970-01-01T00:00:00+00'::timestamptz - RETURNING id - - """, default=NoPayday) + RETURNING ts_end AT TIME ZONE 'UTC' + """, default=NoPayday).replace(tzinfo=aspen.utils.utc) # Move money between Gittip participants. # ======================================= diff --git a/gittip/cli.py b/gittip/cli.py index 040857de0e..c448ac2b56 100644 --- a/gittip/cli.py +++ b/gittip/cli.py @@ -29,7 +29,7 @@ def payday(): from gittip.billing.payday import Payday try: - Payday(db).run() + Payday.start().run() except KeyboardInterrupt: pass except: diff --git a/gittip/wireup.py b/gittip/wireup.py index 8bb780b432..fc63e19aa6 100644 --- a/gittip/wireup.py +++ b/gittip/wireup.py @@ -6,6 +6,7 @@ import aspen import balanced import gittip +import gittip.billing.payday import raven import mandrill from environment import Environment, is_yesish @@ -37,6 +38,7 @@ def db(env): db.register_model(AccountElsewhere) db.register_model(Participant) db.register_model(EmailAddressWithConfirmation) + gittip.billing.payday.Payday.db = db return db From e77c1b894784ecf976831cef2dc570958ec4d88d Mon Sep 17 00:00:00 2001 From: Changaco Date: Sun, 22 Jun 2014 16:33:46 +0200 Subject: [PATCH 13/97] update tests --- tests/py/test_billing_payday.py | 72 ++++++++++++++------------------- tests/py/test_charts_json.py | 2 +- tests/py/test_close.py | 4 +- tests/py/test_history.py | 6 +-- tests/py/test_paydays_json.py | 2 +- tests/py/test_stats.py | 6 +-- 6 files changed, 40 insertions(+), 52 deletions(-) diff --git a/tests/py/test_billing_payday.py b/tests/py/test_billing_payday.py index 054d780cf1..d72121ab21 100644 --- a/tests/py/test_billing_payday.py +++ b/tests/py/test_billing_payday.py @@ -18,20 +18,20 @@ class PaydayHarness(BalancedHarness): def setUp(self): BalancedHarness.setUp(self) - self.payday = Payday(self.db) self.alice = self.make_participant('alice', claimed_time='now') + self.payday = Payday.start() def fetch_payday(self): return self.db.one("SELECT * FROM paydays", back_as=dict) -class TestPayday(PaydayHarness): +class TestPayday(BalancedHarness): @mock.patch('gittip.billing.exchanges.charge_on_balanced') def test_payday_moves_money(self, charge_on_balanced): charge_on_balanced.return_value = (D('10.00'), D('0.68'), "") self.janet.set_tip_to(self.homer, '6.00') # under $10! - self.payday.run() + Payday.start().run() janet = Participant.from_username('janet') homer = Participant.from_username('homer') @@ -48,7 +48,7 @@ def test_payday_doesnt_move_money_from_a_suspicious_account(self, charge_on_bala WHERE username = 'janet' """) self.janet.set_tip_to(self.homer, '6.00') # under $10! - self.payday.run() + Payday.start().run() janet = Participant.from_username('janet') homer = Participant.from_username('homer') @@ -65,7 +65,7 @@ def test_payday_doesnt_move_money_to_a_suspicious_account(self, charge_on_balanc WHERE username = 'homer' """) self.janet.set_tip_to(self.homer, '6.00') # under $10! - self.payday.run() + Payday.start().run() janet = Participant.from_username('janet') homer = Participant.from_username('homer') @@ -75,7 +75,7 @@ def test_payday_doesnt_move_money_to_a_suspicious_account(self, charge_on_balanc def test_payday_moves_money_with_balanced(self): self.janet.set_tip_to(self.homer, '15.00') - self.payday.run() + Payday.start().run() janet = Participant.from_username('janet') homer = Participant.from_username('homer') @@ -100,7 +100,6 @@ def test_payday_moves_money_with_balanced(self): class TestBillingCharges(PaydayHarness): def test_mark_missing_funding(self): - self.payday.start() before = self.fetch_payday() missing_count = before['ncc_missing'] @@ -110,7 +109,6 @@ def test_mark_missing_funding(self): assert after['ncc_missing'] == missing_count + 1 def test_mark_charge_failed(self): - self.payday.start() before = self.fetch_payday() fail_count = before['ncc_failing'] @@ -201,12 +199,13 @@ def test_start_prepare_and_zero_out(self, log): self.make_participant('bob', balance=10, claimed_time=None, pending=1) self.make_participant('carl', balance=10, claimed_time='now', pending=1) - ts_start = self.payday.start() + payday = Payday.start() + ts_start = payday.ts_start get_participants = lambda: self.db.all("SELECT * FROM pay_participants") - self.payday.prepare(ts_start) - self.payday.zero_out_pending(ts_start) + payday.prepare() + payday.zero_out_pending() participants = get_participants() @@ -223,9 +222,10 @@ def test_start_prepare_and_zero_out(self, log): log.reset_mock() # run a second time, we should see it pick up the existing payday - second_ts_start = self.payday.start() - self.payday.prepare(second_ts_start) - self.payday.zero_out_pending(second_ts_start) + payday = Payday.start() + second_ts_start = payday.ts_start + payday.prepare() + payday.zero_out_pending() second_participants = get_participants() assert ts_start == second_ts_start @@ -247,7 +247,6 @@ def test_start_prepare_and_zero_out(self, log): @mock.patch('gittip.billing.payday.log') def test_end(self, log): - self.payday.start() self.payday.end() assert log.called_with('Finished payday.') @@ -258,26 +257,21 @@ def test_end(self, log): assert result == 1 @mock.patch('gittip.billing.payday.log') - @mock.patch('gittip.billing.payday.Payday.start') @mock.patch('gittip.billing.payday.Payday.payin') @mock.patch('gittip.billing.payday.Payday.end') - def test_payday(self, end, payin, init, log): - ts_start = utcnow() - init.return_value = (ts_start,) + def test_payday(self, end, payin, log): greeting = 'Greetings, program! It\'s PAYDAY!!!!' self.payday.run() assert log.called_with(greeting) - assert init.call_count - assert payin.called_with(init.return_value) - assert end.call_count + assert payin.call_count == 1 + assert end.call_count == 1 class TestBillingTransfer(PaydayHarness): def setUp(self): PaydayHarness.setUp(self) - self.payday.start() self.tipper = self.make_participant('lgtest') def test_transfer(self): @@ -378,20 +372,15 @@ def test_record_transfer_invalid_participant(self): class TestPachinko(Harness): - def setUp(self): - Harness.setUp(self) - self.payday = Payday(self.db) - def test_pachinko_pachinkos(self): a_team = self.make_participant('a_team', claimed_time='now', number='plural', balance=20, \ pending=0) a_team.add_member(self.make_participant('alice', claimed_time='now', balance=0, pending=0)) a_team.add_member(self.make_participant('bob', claimed_time='now', balance=0, pending=0)) - ts_start = self.payday.start() - - self.payday.prepare(ts_start) - self.payday.pachinko() + payday = Payday.start() + payday.prepare() + payday.pachinko() assert Participant.from_username('alice').pending == D('0.01') assert Participant.from_username('bob').pending == D('0.01') @@ -403,10 +392,9 @@ def test_pachinko_sees_current_take(self): a_team.add_member(alice) a_team.set_take_for(alice, D('1.00'), alice) - ts_start = self.payday.start() - - self.payday.prepare(ts_start) - self.payday.pachinko() + payday = Payday.start() + payday.prepare() + payday.pachinko() assert Participant.from_username('alice').pending == D('1.00') @@ -417,11 +405,12 @@ def test_pachinko_ignores_take_set_after_payday_starts(self): a_team.add_member(alice) a_team.set_take_for(alice, D('0.33'), alice) - ts_start = self.payday.start() + payday = Payday.start() + a_team.set_take_for(alice, D('1.00'), alice) - self.payday.prepare(ts_start) - self.payday.pachinko() + payday.prepare() + payday.pachinko() assert Participant.from_username('alice').pending == D('0.33') @@ -432,11 +421,12 @@ def test_pachinko_ignores_take_thats_already_been_processed(self): a_team.add_member(alice) a_team.set_take_for(alice, D('0.33'), alice) - ts_start = self.payday.start() + payday = Payday.start() + a_team.set_take_for(alice, D('1.00'), alice) for i in range(4): - self.payday.prepare(ts_start) - self.payday.pachinko() + payday.prepare() + payday.pachinko() assert Participant.from_username('alice').pending == D('0.33') diff --git a/tests/py/test_charts_json.py b/tests/py/test_charts_json.py index 4b6f1462e9..8f8f163d94 100644 --- a/tests/py/test_charts_json.py +++ b/tests/py/test_charts_json.py @@ -29,7 +29,7 @@ def setUp(self): self.bob.set_tip_to(self.carl, '2.00') def run_payday(self): - Payday(self.db).run() + Payday.start().run() def test_no_payday_returns_empty_list(self): diff --git a/tests/py/test_close.py b/tests/py/test_close.py index 45352c44b7..908f4591e6 100644 --- a/tests/py/test_close.py +++ b/tests/py/test_close.py @@ -40,7 +40,7 @@ def test_close_page_is_usually_available(self): assert 'Personal Information' in body def test_close_page_is_not_available_during_payday(self): - Payday(self.db).start() + Payday.start() self.make_participant('alice', claimed_time='now') body = self.client.GET('/alice/account/close', auth_as='alice').body assert 'Personal Information' not in body @@ -59,7 +59,7 @@ def test_can_post_to_close_page(self): assert Participant.from_username('bob').balance == 7 def test_cant_post_to_close_page_during_payday(self): - Payday(self.db).start() + Payday.start() self.make_participant('alice', claimed_time='now') body = self.client.POST('/alice/account/close', auth_as='alice').body assert 'Try Again Later' in body diff --git a/tests/py/test_history.py b/tests/py/test_history.py index 91f386a3d6..f1dc20791b 100644 --- a/tests/py/test_history.py +++ b/tests/py/test_history.py @@ -11,7 +11,7 @@ class TestHistory(Harness): def test_iter_payday_events(self): - Payday(self.db).run() + Payday.start().run() team = self.make_participant('team', number='plural', claimed_time='now', balance=10000) alice = self.make_participant('alice', claimed_time='now', balance=5000) self.db.run(""" @@ -29,7 +29,7 @@ def test_iter_payday_events(self): assert bob.balance == 0 for i in range(2): - Payday(self.db).run() + Payday.start().run() self.db.run(""" UPDATE paydays SET ts_start = ts_start - interval '1 week' @@ -40,7 +40,7 @@ def test_iter_payday_events(self): bob = Participant.from_id(bob.id) assert bob.balance == 12 - Payday(self.db).start() + Payday().start() events = list(iter_payday_events(self.db, bob)) assert len(events) == 8 assert events[0]['kind'] == 'day-open' diff --git a/tests/py/test_paydays_json.py b/tests/py/test_paydays_json.py index a5f82b4969..d27663c8f2 100644 --- a/tests/py/test_paydays_json.py +++ b/tests/py/test_paydays_json.py @@ -9,7 +9,7 @@ class Tests(Harness): def test_paydays_json_gives_paydays(self): - Payday(self.db).start() + Payday.start() self.make_participant("alice") response = self.client.GET("/about/paydays.json") diff --git a/tests/py/test_stats.py b/tests/py/test_stats.py index 8a1651d67c..a0b0a92453 100644 --- a/tests/py/test_stats.py +++ b/tests/py/test_stats.py @@ -117,8 +117,7 @@ def test_stats_description_accurate_during_payday_run(self, utcnow): env = wireup.env() wireup.billing(env) - payday = Payday(self.db) - payday.start() + payday = Payday.start() body = self.get_stats_page() assert "is changing hands right now!" in body, body @@ -133,8 +132,7 @@ def test_stats_description_accurate_outside_of_payday(self, utcnow): self.client.hydrate_website() - payday = Payday(self.db) - payday.start() + payday = Payday.start() body = self.get_stats_page() assert "is ready for this Thursday" in body, body From 0b736b063e1da601cd2022f622d5ebd0c0533b56 Mon Sep 17 00:00:00 2001 From: Changaco Date: Mon, 23 Jun 2014 11:52:28 +0200 Subject: [PATCH 14/97] move constructor to the top --- gittip/billing/payday.py | 66 ++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/gittip/billing/payday.py b/gittip/billing/payday.py index 637e6fdc4d..4234c767f5 100644 --- a/gittip/billing/payday.py +++ b/gittip/billing/payday.py @@ -44,6 +44,39 @@ class Payday(object): """ + @classmethod + def start(cls): + """Try to start a new Payday. + + If there is a Payday that hasn't finished yet, then the UNIQUE + constraint on ts_end will kick in and notify us of that. In that case + we load the existing Payday and work on it some more. We use the start + time of the current Payday to synchronize our work. + + """ + try: + d = cls.db.one(""" + INSERT INTO paydays DEFAULT VALUES + RETURNING id, (ts_start AT TIME ZONE 'UTC') AS ts_start + """, back_as=dict) + log("Starting a new payday.") + except IntegrityError: # Collision, we have a Payday already. + d = cls.db.one(""" + SELECT id, (ts_start AT TIME ZONE 'UTC') AS ts_start + FROM paydays + WHERE ts_end='1970-01-01T00:00:00+00'::timestamptz + """, back_as=dict) + log("Picking up with an existing payday.") + + d['ts_start'] = d['ts_start'].replace(tzinfo=aspen.utils.utc) + + log("Payday started at %s." % d['ts_start']) + + payday = Payday() + payday.__dict__.update(d) + return payday + + def run(self): """This is the starting point for payday. @@ -78,39 +111,6 @@ def run(self): log(aspen.utils.to_age(_start, fmt_past=fmt_past)) - @classmethod - def start(cls): - """Try to start a new Payday. - - If there is a Payday that hasn't finished yet, then the UNIQUE - constraint on ts_end will kick in and notify us of that. In that case - we load the existing Payday and work on it some more. We use the start - time of the current Payday to synchronize our work. - - """ - try: - d = cls.db.one(""" - INSERT INTO paydays DEFAULT VALUES - RETURNING id, (ts_start AT TIME ZONE 'UTC') AS ts_start - """, back_as=dict) - log("Starting a new payday.") - except IntegrityError: # Collision, we have a Payday already. - d = cls.db.one(""" - SELECT id, (ts_start AT TIME ZONE 'UTC') AS ts_start - FROM paydays - WHERE ts_end='1970-01-01T00:00:00+00'::timestamptz - """, back_as=dict) - log("Picking up with an existing payday.") - - d['ts_start'] = d['ts_start'].replace(tzinfo=aspen.utils.utc) - - log("Payday started at %s." % d['ts_start']) - - payday = Payday() - payday.__dict__.update(d) - return payday - - def prepare(self): """Prepare the DB: we need temporary tables with indexes. """ From ee2315619fad4685fd911293d39e7bd294e99bdf Mon Sep 17 00:00:00 2001 From: Changaco Date: Sun, 8 Jun 2014 13:33:06 +0200 Subject: [PATCH 15/97] implement creating and capturing a card hold --- gittip/billing/exchanges.py | 83 +++++++++++++++++++++---------------- 1 file changed, 47 insertions(+), 36 deletions(-) diff --git a/gittip/billing/exchanges.py b/gittip/billing/exchanges.py index 1ad4d6c99d..6f68da7bba 100644 --- a/gittip/billing/exchanges.py +++ b/gittip/billing/exchanges.py @@ -118,12 +118,11 @@ def ach_credit(db, participant, withhold, minimum_credit=MINIMUM_CREDIT): return error -def charge(db, participant, amount): - """Charge the participants credit card. +def create_card_hold(db, participant, amount): + """Create a hold on the participant's credit card. - This is the only place where we actually charge credit cards. Amount - should be the nominal amount. We'll compute Gittip's fee below this - function and add it to amount to end up with charge_amount. + Amount should be the nominal amount. We'll compute Gittip's fee below + this function and add it to amount to end up with charge_amount. """ typecheck(amount, Decimal) @@ -149,33 +148,16 @@ def charge(db, participant, amount): # Go to Balanced. # =============== - things = charge_on_balanced( username - , balanced_customer_href - , amount - ) - charge_amount, fee, error = things - - amount = charge_amount - fee # account for possible rounding under - # charge_on_* - - record_exchange(db, 'bill', amount, fee, error, participant) - return error - - -def charge_on_balanced(username, balanced_customer_href, amount): - """We have a purported balanced_customer_href. Try to use it. - """ - typecheck( username, unicode - , balanced_customer_href, unicode - , amount, Decimal - ) - - cents, msg, charge_amount, fee = _prep_hit(amount) - msg = msg % (username, "Balanced") + cents, amount_str, charge_amount, fee = _prep_hit(amount) + msg = "Holding " + amount_str + " on Balanced for " + username + " ... " + hold = None try: - customer = balanced.Customer.fetch(balanced_customer_href) - customer.cards.one().debit(amount=cents, description=username) + card = balanced.Customer.fetch(balanced_customer_href).cards.one() + hold = card.hold( amount=cents + , description=username + , meta=dict(participant_id=participant.id, state='new') + ) log(msg + "succeeded.") error = "" except balanced.exc.HTTPError as err: @@ -185,8 +167,38 @@ def charge_on_balanced(username, balanced_customer_href, amount): if error: log(msg + "failed: %s" % error) + record_exchange(db, 'bill', None, None, error, participant) + + return hold, error + - return charge_amount, fee, error +def capture_card_hold(db, participant, amount, hold): + """Capture the previously created hold on the participant's credit card. + """ + typecheck( hold, balanced.CardHold + , amount, Decimal + ) + + username = participant.username + + cents, amount_str, charge_amount, fee = _prep_hit(amount) + + hold.capture(amount=cents, description=username) + hold.meta['state'] = 'captured' + hold.save() + + log("Captured " + amount_str + " on Balanced for " + username) + + amount = charge_amount - fee # account for possible rounding + record_exchange(db, 'bill', amount, fee, '', participant) + + +def cancel_card_hold(hold): + """Cancel the previously created hold on the participant's credit card. + """ + hold.is_void = True + hold.meta['state'] = 'cancelled' + hold.save() def _prep_hit(unrounded): @@ -195,8 +207,7 @@ def _prep_hit(unrounded): cents This is passed to the payment processor charge API. This is the value that is actually charged to the participant. It's an int. - msg A log message with a couple %s to be filled in by the - caller. + amount_str A detailed string representation of the amount. upcharged Decimal dollar equivalent to `cents'. fee Decimal dollar amount of the fee portion of `upcharged'. @@ -212,10 +223,10 @@ def _prep_hit(unrounded): upcharged, fee = upcharge(rounded) cents = int(upcharged * 100) - msg = "Charging %%s %d cents ($%s%s + $%s fee = $%s) on %%s ... " - msg %= cents, rounded, also_log, fee, upcharged + amount_str = "%d cents ($%s%s + $%s fee = $%s)" + amount_str %= cents, rounded, also_log, fee, upcharged - return cents, msg, upcharged, fee + return cents, amount_str, upcharged, fee def record_exchange(db, kind, amount, fee, error, participant): From 7b1db71482a696e703e7242cf5af0992a67e918a Mon Sep 17 00:00:00 2001 From: Changaco Date: Thu, 19 Jun 2014 10:40:52 +0200 Subject: [PATCH 16/97] modify DB self checks to work on cursors --- gittip/models/__init__.py | 441 +++++++++++++++++++------------------- 1 file changed, 225 insertions(+), 216 deletions(-) diff --git a/gittip/models/__init__.py b/gittip/models/__init__.py index a78fc4da38..789f8d0ce7 100644 --- a/gittip/models/__init__.py +++ b/gittip/models/__init__.py @@ -9,227 +9,236 @@ from postgres import Postgres import psycopg2.extras + class GittipDB(Postgres): def self_check(self): - """ - Runs all available self checks on the database. - """ - self._check_balances() - self._check_tips() - self._check_orphans() - self._check_orphans_no_tips() - self._check_paydays_volumes() - self._check_claimed_not_locked() - - def _check_tips(self): - """ - Checks that there are no rows in tips with duplicate (tipper, tippee, mtime). - - https://github.com/gittip/www.gittip.com/issues/1704 - """ - conflicting_tips = self.one(""" - SELECT count(*) - FROM - ( - SELECT * FROM tips - EXCEPT - SELECT DISTINCT ON(tipper, tippee, mtime) * - FROM tips - ORDER BY tipper, tippee, mtime - ) AS foo - """) - assert conflicting_tips == 0 + with self.get_cursor() as cursor: + check_db(cursor) - def _check_balances(self): - """ - Recalculates balances for all participants from transfers and exchanges. - https://github.com/gittip/www.gittip.com/issues/1118 - """ - with self.get_cursor() as cursor: - if cursor.one("select exists (select * from paydays where ts_end < ts_start) as running"): - # payday is running and the query bellow does not account for pending - return - b = cursor.one(""" - select count(*) - from ( - select username, sum(a) as balance - from ( - select participant as username, sum(amount) as a - from exchanges - where amount > 0 - group by participant - - union all - - select participant as username, sum(amount-fee) as a - from exchanges - where amount < 0 - group by participant - - union all - - select tipper as username, sum(-amount) as a - from transfers - group by tipper - - union all - - select tippee as username, sum(amount) as a - from transfers - group by tippee - ) as foo - group by username - - except - - select username, balance - from participants - ) as foo2 - """) - assert b == 0, "conflicting balances: {}".format(b) - - def _check_orphans(self): - """ - Finds participants that - * does not have corresponding elsewhere account - * have not been absorbed by other participant - - These are broken because new participants arise from elsewhere - and elsewhere is detached only by take over which makes a note - in absorptions if it removes the last elsewhere account. - - Especially bad case is when also claimed_time is set because - there must have been elsewhere account attached and used to sign in. - - https://github.com/gittip/www.gittip.com/issues/617 - """ - orphans = self.all(""" - select username - from participants - where not exists (select * from elsewhere where elsewhere.participant=username) - and not exists (select * from absorptions where archived_as=username) - """) - assert len(orphans) == 0, "missing elsewheres: {}".format(list(orphans)) - - def _check_orphans_no_tips(self): - """ - Finds participants - * without elsewhere account attached - * having non zero outstanding tip - - This should not happen because when we remove the last elsewhere account - in take_over we also zero out all tips. - """ - tips_with_orphans = self.all(""" - WITH orphans AS ( - SELECT username FROM participants - WHERE NOT EXISTS (SELECT 1 FROM elsewhere WHERE participant=username) - ), valid_tips AS ( - SELECT * FROM ( - SELECT DISTINCT ON (tipper, tippee) * - FROM tips - ORDER BY tipper, tippee, mtime DESC - ) AS foo - WHERE amount > 0 - ) - SELECT id FROM valid_tips - WHERE tipper IN (SELECT * FROM orphans) - OR tippee IN (SELECT * FROM orphans) - """) - known = set([25206, 46266]) # '4c074000c7bc', 'naderman', '3.00' - real = set(tips_with_orphans) - known - assert len(real) == 0, real - - def _check_paydays_volumes(self): - """ - Recalculate *_volume fields in paydays table using exchanges table. - """ - with self.get_cursor() as cursor: - if cursor.one("select exists (select * from paydays where ts_end < ts_start) as running"): - # payday is running - return - charge_volume = cursor.all(""" - select * from ( - select id, ts_start, charge_volume, ( - select coalesce(sum(amount+fee), 0) - from exchanges - where timestamp > ts_start - and timestamp < ts_end - and amount > 0 - and recorder is null - ) as ref - from paydays - order by id - ) as foo - where charge_volume != ref - """) - assert len(charge_volume) == 0 - - charge_fees_volume = cursor.all(""" - select * from ( - select id, ts_start, charge_fees_volume, ( - select coalesce(sum(fee), 0) - from exchanges - where timestamp > ts_start - and timestamp < ts_end - and amount > 0 - and recorder is null - ) as ref - from paydays - order by id - ) as foo - where charge_fees_volume != ref - """) - assert len(charge_fees_volume) == 0 - - ach_volume = cursor.all(""" - select * from ( - select id, ts_start, ach_volume, ( - select coalesce(sum(amount), 0) - from exchanges - where timestamp > ts_start - and timestamp < ts_end - and amount < 0 - and recorder is null - ) as ref - from paydays - order by id - ) as foo - where ach_volume != ref - """) - assert len(ach_volume) == 0 - - ach_fees_volume = cursor.all(""" - select * from ( - select id, ts_start, ach_fees_volume, ( - select coalesce(sum(fee), 0) - from exchanges - where timestamp > ts_start - and timestamp < ts_end - and amount < 0 - and recorder is null - ) as ref - from paydays - order by id - ) as foo - where ach_fees_volume != ref - """) - assert len(ach_fees_volume) == 0 - - def _check_claimed_not_locked(self): - locked = self.all(""" - SELECT participant - FROM elsewhere - WHERE EXISTS ( - SELECT * - FROM participants - WHERE username=participant - AND claimed_time IS NOT NULL - ) AND is_locked - """) - assert len(locked) == 0 +def check_db(cursor): + """Runs all available self checks on the given cursor. + """ + _check_balances(cursor) + _check_tips(cursor) + _check_orphans(cursor) + _check_orphans_no_tips(cursor) + _check_paydays_volumes(cursor) + _check_claimed_not_locked(cursor) + + +def _check_tips(cursor): + """ + Checks that there are no rows in tips with duplicate (tipper, tippee, mtime). + + https://github.com/gittip/www.gittip.com/issues/1704 + """ + conflicting_tips = cursor.one(""" + SELECT count(*) + FROM + ( + SELECT * FROM tips + EXCEPT + SELECT DISTINCT ON(tipper, tippee, mtime) * + FROM tips + ORDER BY tipper, tippee, mtime + ) AS foo + """) + assert conflicting_tips == 0 + + +def _check_balances(cursor): + """ + Recalculates balances for all participants from transfers and exchanges. + + https://github.com/gittip/www.gittip.com/issues/1118 + """ + if cursor.one("select exists (select * from paydays where ts_end < ts_start) as running"): + # payday is running and the query bellow does not account for pending + return + b = cursor.one(""" + select count(*) + from ( + select username, sum(a) as balance + from ( + select participant as username, sum(amount) as a + from exchanges + where amount > 0 + group by participant + + union all + + select participant as username, sum(amount-fee) as a + from exchanges + where amount < 0 + group by participant + + union all + + select tipper as username, sum(-amount) as a + from transfers + group by tipper + + union all + + select tippee as username, sum(amount) as a + from transfers + group by tippee + ) as foo + group by username + + except + + select username, balance + from participants + ) as foo2 + """) + assert b == 0, "conflicting balances: {}".format(b) + + +def _check_orphans(cursor): + """ + Finds participants that + * does not have corresponding elsewhere account + * have not been absorbed by other participant + + These are broken because new participants arise from elsewhere + and elsewhere is detached only by take over which makes a note + in absorptions if it removes the last elsewhere account. + + Especially bad case is when also claimed_time is set because + there must have been elsewhere account attached and used to sign in. + + https://github.com/gittip/www.gittip.com/issues/617 + """ + orphans = cursor.all(""" + select username + from participants + where not exists (select * from elsewhere where elsewhere.participant=username) + and not exists (select * from absorptions where archived_as=username) + """) + assert len(orphans) == 0, "missing elsewheres: {}".format(list(orphans)) + + +def _check_orphans_no_tips(cursor): + """ + Finds participants + * without elsewhere account attached + * having non zero outstanding tip + + This should not happen because when we remove the last elsewhere account + in take_over we also zero out all tips. + """ + tips_with_orphans = cursor.all(""" + WITH orphans AS ( + SELECT username FROM participants + WHERE NOT EXISTS (SELECT 1 FROM elsewhere WHERE participant=username) + ), valid_tips AS ( + SELECT * FROM ( + SELECT DISTINCT ON (tipper, tippee) * + FROM tips + ORDER BY tipper, tippee, mtime DESC + ) AS foo + WHERE amount > 0 + ) + SELECT id FROM valid_tips + WHERE tipper IN (SELECT * FROM orphans) + OR tippee IN (SELECT * FROM orphans) + """) + known = set([25206, 46266]) # '4c074000c7bc', 'naderman', '3.00' + real = set(tips_with_orphans) - known + assert len(real) == 0, real + + +def _check_paydays_volumes(cursor): + """ + Recalculate *_volume fields in paydays table using exchanges table. + """ + if cursor.one("select exists (select * from paydays where ts_end < ts_start) as running"): + # payday is running + return + charge_volume = cursor.all(""" + select * from ( + select id, ts_start, charge_volume, ( + select coalesce(sum(amount+fee), 0) + from exchanges + where timestamp > ts_start + and timestamp < ts_end + and amount > 0 + and recorder is null + ) as ref + from paydays + order by id + ) as foo + where charge_volume != ref + """) + assert len(charge_volume) == 0 + + charge_fees_volume = cursor.all(""" + select * from ( + select id, ts_start, charge_fees_volume, ( + select coalesce(sum(fee), 0) + from exchanges + where timestamp > ts_start + and timestamp < ts_end + and amount > 0 + and recorder is null + ) as ref + from paydays + order by id + ) as foo + where charge_fees_volume != ref + """) + assert len(charge_fees_volume) == 0 + + ach_volume = cursor.all(""" + select * from ( + select id, ts_start, ach_volume, ( + select coalesce(sum(amount), 0) + from exchanges + where timestamp > ts_start + and timestamp < ts_end + and amount < 0 + and recorder is null + ) as ref + from paydays + order by id + ) as foo + where ach_volume != ref + """) + assert len(ach_volume) == 0 + + ach_fees_volume = cursor.all(""" + select * from ( + select id, ts_start, ach_fees_volume, ( + select coalesce(sum(fee), 0) + from exchanges + where timestamp > ts_start + and timestamp < ts_end + and amount < 0 + and recorder is null + ) as ref + from paydays + order by id + ) as foo + where ach_fees_volume != ref + """) + assert len(ach_fees_volume) == 0 + + +def _check_claimed_not_locked(cursor): + locked = cursor.all(""" + SELECT participant + FROM elsewhere + WHERE EXISTS ( + SELECT * + FROM participants + WHERE username=participant + AND claimed_time IS NOT NULL + ) AND is_locked + """) + assert len(locked) == 0 def add_event(c, type, payload): From 69c06c84110868969e714f3efe24fbe719bddc22 Mon Sep 17 00:00:00 2001 From: Changaco Date: Thu, 19 Jun 2014 10:46:23 +0200 Subject: [PATCH 17/97] we want to be able to run that check during payday --- gittip/models/__init__.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/gittip/models/__init__.py b/gittip/models/__init__.py index 789f8d0ce7..6d7f4d7d9b 100644 --- a/gittip/models/__init__.py +++ b/gittip/models/__init__.py @@ -54,9 +54,6 @@ def _check_balances(cursor): https://github.com/gittip/www.gittip.com/issues/1118 """ - if cursor.one("select exists (select * from paydays where ts_end < ts_start) as running"): - # payday is running and the query bellow does not account for pending - return b = cursor.one(""" select count(*) from ( From 70319321b07ccbe96eb2ea35cd9a78329e51bcf4 Mon Sep 17 00:00:00 2001 From: Changaco Date: Wed, 18 Jun 2014 18:52:06 +0200 Subject: [PATCH 18/97] update comments --- gittip/billing/payday.py | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/gittip/billing/payday.py b/gittip/billing/payday.py index 4234c767f5..9625dcabad 100644 --- a/gittip/billing/payday.py +++ b/gittip/billing/payday.py @@ -1,19 +1,12 @@ -"""This is Gittip's payday algorithm. I would appreciate feedback on it. - -The payday algorithm is designed to be crash-resistant and parallelizable, but -it's not eventually consistent in the strict sense (iinm) because consistency -is always apodeictically knowable. +"""This is Gittip's payday algorithm. Exchanges (moving money between Gittip and the outside world) and transfers (moving money amongst Gittip users) happen within an isolated event called -payday. This event has duration (it's not punctiliar). It is started -transactionally, and it ends transactionally, and inside of it, exchanges and -transfers happen transactionally (though the link between our db and our -processor's db could be tightened up; see #213). Exchanges immediately affect -the participant's balance, but transfers accrue against a "pending" column in -the database. Once the payday event has completed successfully, it ends with -the pending column being applied to the balance column and reset to NULL in a -single transaction. +payday. This event has duration (it's not punctiliar). + +Payday is designed to be crash-resistant. Everything that can be rolled back +happens inside a single DB transaction. Exchanges cannot be rolled back, so they +immediately affect the participant's balance. """ from __future__ import unicode_literals From 9a16cc4409c1d639b8c094aff7cf212b1300cd2b Mon Sep 17 00:00:00 2001 From: Changaco Date: Mon, 23 Jun 2014 18:45:13 +0200 Subject: [PATCH 19/97] add a `stage` column to the `paydays` table --- branch.sql | 3 +++ gittip/billing/payday.py | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) create mode 100644 branch.sql diff --git a/branch.sql b/branch.sql new file mode 100644 index 0000000000..399291bd05 --- /dev/null +++ b/branch.sql @@ -0,0 +1,3 @@ +BEGIN; + ALTER TABLE paydays ADD COLUMN stage integer DEFAULT 0; +END; diff --git a/gittip/billing/payday.py b/gittip/billing/payday.py index 9625dcabad..2994c873a1 100644 --- a/gittip/billing/payday.py +++ b/gittip/billing/payday.py @@ -50,12 +50,12 @@ def start(cls): try: d = cls.db.one(""" INSERT INTO paydays DEFAULT VALUES - RETURNING id, (ts_start AT TIME ZONE 'UTC') AS ts_start + RETURNING id, (ts_start AT TIME ZONE 'UTC') AS ts_start, stage """, back_as=dict) log("Starting a new payday.") except IntegrityError: # Collision, we have a Payday already. d = cls.db.one(""" - SELECT id, (ts_start AT TIME ZONE 'UTC') AS ts_start + SELECT id, (ts_start AT TIME ZONE 'UTC') AS ts_start, stage FROM paydays WHERE ts_end='1970-01-01T00:00:00+00'::timestamptz """, back_as=dict) From 6570951c868987a082f48f05f6044dd39347964f Mon Sep 17 00:00:00 2001 From: Changaco Date: Tue, 24 Jun 2014 14:52:34 +0200 Subject: [PATCH 20/97] rewrite the payin loop in SQL fixes #1486 --- gittip/billing/payday.py | 580 ++++++++++++++++++--------------------- 1 file changed, 266 insertions(+), 314 deletions(-) diff --git a/gittip/billing/payday.py b/gittip/billing/payday.py index 2994c873a1..da0947b8f2 100644 --- a/gittip/billing/payday.py +++ b/gittip/billing/payday.py @@ -11,13 +11,17 @@ """ from __future__ import unicode_literals -from decimal import Decimal +from balanced import CardHold import aspen.utils from aspen import log -from aspen.utils import typecheck -from gittip.billing.exchanges import ach_credit, charge -from gittip.exceptions import NegativeBalance, NoBalancedCustomerHref, NotWhitelisted +from gittip.billing.exchanges import ( + ach_credit, cancel_card_hold, capture_card_hold, create_card_hold, upcharge +) +from gittip.exceptions import ( + NegativeBalance, NoBalancedCustomerHref, NotWhitelisted +) +from gittip.models import check_db from psycopg2 import IntegrityError @@ -83,13 +87,7 @@ def run(self): _start = aspen.utils.utcnow() log("Greetings, program! It's PAYDAY!!!!") - self.prepare() - self.zero_out_pending() - self.payin() - self.move_pending_to_balance_for_teams() - self.pachinko() - self.clear_pending_to_balance() self.payout() self.update_stats() self.update_receiving_amounts() @@ -104,25 +102,44 @@ def run(self): log(aspen.utils.to_age(_start, fmt_past=fmt_past)) - def prepare(self): - """Prepare the DB: we need temporary tables with indexes. + def payin(self): + """The first stage of payday where we charge credit cards and transfer + money internally between participants. """ - self.db.run(""" + with self.db.get_cursor() as cursor: + self.prepare(cursor, self.ts_start) + holds = self.create_card_holds(cursor) + self.transfer_tips(cursor) + self.transfer_takes(cursor, self.ts_start) + self.settle_card_holds(cursor, holds) + self.update_balances(cursor) + check_db(cursor) + + + @staticmethod + def prepare(cursor, ts_start): + """Prepare the DB: we need temporary tables with indexes and triggers. + """ + cursor.run(""" + + -- Create the necessary temporary tables and indexes DROP TABLE IF EXISTS pay_participants CASCADE; CREATE TEMPORARY TABLE pay_participants AS - SELECT username + SELECT id + , username , claimed_time - , number - , is_suspicious - , balance + , balance AS old_balance + , balance AS new_balance , balanced_customer_href , last_bill_result + , is_suspicious FROM participants WHERE is_suspicious IS NOT true AND claimed_time < %(ts_start)s ORDER BY claimed_time; + CREATE UNIQUE INDEX ON pay_participants (id); CREATE UNIQUE INDEX ON pay_participants (username); DROP TABLE IF EXISTS pay_transfers CASCADE; @@ -152,18 +169,197 @@ def prepare(self): CREATE INDEX ON pay_tips (tipper); CREATE INDEX ON pay_tips (tippee); + ALTER TABLE pay_tips ADD COLUMN is_funded boolean; ALTER TABLE pay_participants ADD COLUMN giving_today numeric(35,2); UPDATE pay_participants - SET giving_today = ( + SET giving_today = COALESCE(( SELECT sum(amount) FROM pay_tips WHERE tipper = username + ), 0); + + DROP TABLE IF EXISTS pay_takes; + CREATE TEMPORARY TABLE pay_takes + ( team text + , member text + , amount numeric(35,2) + ); + + + -- Prepare a statement that makes and records a transfer + + CREATE OR REPLACE FUNCTION transfer(text, text, numeric, context_type) + RETURNS void AS $$ + BEGIN + UPDATE pay_participants + SET new_balance = (new_balance - $3) + WHERE username = $1; + UPDATE pay_participants + SET new_balance = (new_balance + $3) + WHERE username = $2; + INSERT INTO transfers + (tipper, tippee, amount, context) + VALUES ($1, $2, $3, $4); + END; + $$ LANGUAGE plpgsql; + + + -- Create a trigger to process tips + + CREATE OR REPLACE FUNCTION process_tip() RETURNS trigger AS $$ + BEGIN + EXECUTE transfer(NEW.tipper, NEW.tippee, NEW.amount, 'tip'); + RETURN NULL; + END; + $$ LANGUAGE plpgsql; + + CREATE TRIGGER process_tip AFTER UPDATE OF is_funded ON pay_tips + FOR EACH ROW + WHEN (NEW.is_funded IS true AND OLD.is_funded IS NOT true) + EXECUTE PROCEDURE process_tip(); + + + -- Create a trigger to process takes + + CREATE OR REPLACE FUNCTION process_take() RETURNS trigger AS $$ + DECLARE + actual_amount numeric(35,2); + team_balance numeric(35,2); + BEGIN + team_balance := ( + SELECT new_balance + FROM pay_participants + WHERE username = NEW.team + ); + actual_amount := NEW.amount; + IF (team_balance < NEW.amount) THEN + actual_amount := team_balance; + END IF; + EXECUTE transfer(NEW.team, NEW.member, actual_amount, 'take'); + RETURN NULL; + END; + $$ LANGUAGE plpgsql; + + CREATE TRIGGER process_take AFTER INSERT ON pay_takes + FOR EACH ROW EXECUTE PROCEDURE process_take(); + + + -- Save the stats we already have + + UPDATE paydays + SET nparticipants = (SELECT count(*) FROM pay_participants) + , ncc_missing = ( + SELECT count(*) + FROM pay_participants + WHERE old_balance < giving_today + AND ( balanced_customer_href IS NULL + OR + last_bill_result IS NULL + ) + ) + + """, dict(ts_start=ts_start)) + log('Prepared the DB.') + + + @staticmethod + def fetch_card_holds(participant_ids): + holds = {} + for hold in CardHold.query.filter(CardHold.f.meta.state == 'new'): + state = 'new' + if hold.failure_reason: + state = 'failed' + elif hold.voided_at: + state = 'cancelled' + elif getattr(hold, 'debit_href', None): + state = 'captured' + if state != 'new': + hold.meta['state'] = state + hold.save() + continue + p_id = int(hold.meta['participant_id']) + if p_id in participant_ids: + holds[p_id] = hold + else: + cancel_card_hold(hold) + return holds + + + def create_card_holds(self, cursor): + + # Get the list of participants to create card holds for + participants = cursor.all(""" + SELECT * + FROM pay_participants + WHERE old_balance < giving_today + AND balanced_customer_href IS NOT NULL + AND last_bill_result IS NOT NULL + """) + if not participants: + return {} + + # Fetch existing holds + participant_ids = set(p.id for p in participants) + holds = self.fetch_card_holds(participant_ids) + + # Create new holds and check amounts of existing ones + for p in participants: + amount = p.giving_today + if p.old_balance < 0: + amount -= p.old_balance + if p.id in holds: + charge_amount = upcharge(amount)[0] + if holds[p.id].amount >= charge_amount * 100: + continue + else: + # The amount is too low, cancel the hold and make a new one + cancel_card_hold(holds.pop(p.id)) + hold, error = create_card_hold(self.db, p, amount) + if error: + self.mark_charge_failed(cursor) + else: + holds[p.id] = hold + + # Update the values of last_bill_result in our temporary table + cursor.run(""" + UPDATE pay_participants p + SET last_bill_result = p2.last_bill_result + FROM participants p2 + WHERE p.id = p2.id + """) + + return holds + + + @staticmethod + def transfer_tips(cursor): + cursor.run(""" + + UPDATE pay_tips t + SET is_funded = true + FROM pay_participants p + WHERE p.username = t.tipper + AND p.last_bill_result = ''; + + UPDATE pay_tips t + SET is_funded = true + WHERE is_funded IS NOT true + AND amount <= ( + SELECT new_balance + FROM pay_participants p + WHERE p.username = t.tipper ); - DROP TABLE IF EXISTS pay_takes CASCADE; - CREATE TEMPORARY TABLE pay_takes AS - SELECT team, member, amount, ctime + """) + + + @staticmethod + def transfer_takes(cursor, ts_start): + cursor.run(""" + + INSERT INTO pay_takes + SELECT team, member, amount FROM ( SELECT DISTINCT ON (team, member) team, member, amount, ctime FROM takes @@ -178,90 +374,62 @@ def prepare(self): WHERE t.team = t2.tipper AND t.member = t2.tippee AND context = 'take' - ) IS NULL; - - CREATE INDEX ON pay_takes (team); - - """, dict(ts_start=self.ts_start)) - log('Prepared the DB.') - - - def zero_out_pending(self): - """Given a timestamp, zero out the pending column. - - We keep track of balance changes as a result of Payday in the pending - column, and then move them over to the balance column in one big - transaction at the end of Payday. - - """ - START_PENDING = """\ - - UPDATE participants - SET pending=0.00 - WHERE pending IS NULL - AND claimed_time < %s + ) IS NULL + ORDER BY t.team, t.ctime DESC; - """ - self.db.run(START_PENDING, (self.ts_start,)) - log("Zeroed out the pending column.") - return None + """, dict(ts_start=ts_start)) - def payin(self): - """Do the payin side of Payday. - """ - i = 0 - log("Starting payin loop.") - participants = self.db.all(""" - SELECT * FROM pay_participants WHERE giving_today > 0 + def settle_card_holds(self, cursor, holds): + participants = cursor.all(""" + SELECT * + FROM pay_participants + WHERE new_balance < 0 """) - for i, participant in enumerate(participants, start=1): - if i % 100 == 0: - log("Payin done for %d participants." % i) - self.charge_and_or_transfer(participant) - log("Did payin for %d participants." % i) - - def pachinko(self): + # Capture holds to bring balances back up to (at least) zero i = 0 - participants = self.db.all(""" - SELECT * FROM pay_participants WHERE number = 'plural' - """) - for i, participant in enumerate(participants, start=1): - if i % 100 == 0: - log("Pachinko done for %d participants." % i) - - available = participant.balance - log("Pachinko out from %s with $%s." % ( participant.username - , available - )) - - def tip(tippee, amount): - tip = {} - tip['tipper'] = participant.username - tip['tippee'] = tippee - tip['amount'] = amount - self.tip( participant - , tip - , pachinko=True - ) - - takes = self.db.all(""" - SELECT * FROM pay_takes WHERE team = %s ORDER BY ctime DESC - """, (participant.username,), back_as=dict) - - for take in takes: - amount = min(take['amount'], available) - available -= amount - tip(take['member'], amount) - if available == 0: - break + for p in participants: + assert p.id in holds + amount = -p.new_balance + capture_card_hold(self.db, p, amount, holds.pop(p.id)) + i += 1 + log("Captured %i card holds." % i) + + # Cancel the remaining holds + for hold in holds.values(): + cancel_card_hold(hold) + log("Canceled %i card holds." % len(holds)) + + + @staticmethod + def update_balances(cursor): + participants = cursor.all(""" + + UPDATE participants p + SET balance = (balance + p2.new_balance - p2.old_balance) + FROM pay_participants p2 + WHERE p.id = p2.id + AND p2.new_balance <> p2.old_balance + RETURNING p.id + , p.username + , balance AS new_balance + , ( SELECT balance + FROM participants p3 + WHERE p3.id = p.id + ) AS cur_balance; - log("Did pachinko for %d participants." % i) + """) + # Check that balances aren't becoming (more) negative + for p in participants: + if p.new_balance < 0 and p.new_balance < p.cur_balance: + raise NegativeBalance(p) + log("Updated the balances of %i participants." % len(participants)) def payout(self): - """Do the payout side of Payday. + """This is the second stage of payday in which we send money out to the + bank accounts of participants. """ i = 0 log("Starting payout loop.") @@ -284,91 +452,6 @@ def payout(self): log("Did payout for %d participants." % i) - def charge_and_or_transfer(self, participant): - """Given one participant record, pay their day. - - Charge each participants' credit card if needed before transfering - money between Gittip accounts. - - """ - short = participant.giving_today - participant.balance - if short > 0: - - # The participant's Gittip account is short the amount needed to - # fund all their tips. Let's try pulling in money from their credit - # card. If their credit card fails we'll forge ahead, in case they - # have a positive Gittip balance already that can be used to fund - # at least *some* tips. The charge method will have set - # last_bill_result to a non-empty string if the card did fail. - - try: - error = charge(self.db, participant, short) - if error: - self.mark_charge_failed() - except NoBalancedCustomerHref: - self.mark_missing_funding() - except NotWhitelisted: - if participant.is_suspicious is None: - log("UNREVIEWED: %s" % participant.username) - - tips = self.db.all(""" - SELECT * FROM pay_tips WHERE tipper = %s - """, (participant.username,), back_as=dict) - - nsuccessful_tips = 0 - for tip in tips: - result = self.tip(participant, tip) - if result >= 0: - nsuccessful_tips += result - else: - break - - - def move_pending_to_balance_for_teams(self): - """Transfer pending into balance for teams. - - We do this because debit_participant operates against balance, not - pending. This is because credit card charges go directly into balance - on the first (payin) loop. - - """ - self.db.run("""\ - - UPDATE participants - SET balance = (balance + pending) - , pending = 0 - WHERE pending IS NOT NULL - AND number='plural' - - """) - # "Moved" instead of "cleared" because we don't also set to null. - log("Moved pending to balance for teams. Ready for pachinko.") - - - def clear_pending_to_balance(self): - """Transfer pending into balance, setting pending to NULL. - - Any users that were created while the payin loop was running will have - pending NULL (the default). If we try to add that to balance we'll get - a NULL (0.0 + NULL = NULL), and balance has a NOT NULL constraint. - Hence the where clause. See: - - https://github.com/gittip/www.gittip.com/issues/170 - - """ - - self.db.run("""\ - - UPDATE participants - SET balance = (balance + pending) - , pending = NULL - WHERE pending IS NOT NULL - - """) - # "Cleared" instead of "moved because we also set to null. - log("Cleared pending to balance. Ready for payouts.") - - def update_stats(self): self.db.run("""\ @@ -410,7 +493,6 @@ def update_stats(self): SELECT tippee FROM our_transfers ) AS foo ) - , nparticipants = (SELECT count(*) FROM pay_participants) , ntippers = (SELECT count(DISTINCT tipper) FROM our_transfers) , ntips = (SELECT count(*) FROM our_tips) , npachinko = (SELECT count(*) FROM our_pachinkos) @@ -461,143 +543,13 @@ def end(self): """, default=NoPayday).replace(tzinfo=aspen.utils.utc) - # Move money between Gittip participants. - # ======================================= - - def tip(self, participant, tip, pachinko=False): - """Given dict, dict, and datetime, log and return int. - - Return values: - - | 0 if no valid tip available or tip has not been claimed - | 1 if tip is valid - | -1 if transfer fails and we cannot continue - - """ - msg = "$%s from %s to %s%s." - msg %= ( tip['amount'] - , participant.username - , tip['tippee'] - , " (pachinko)" if pachinko else "" - ) - - if tip['amount'] == 0: - - # The tips table contains a record for every time you click a tip - # button. So if you click $0.25 then $3.00 then $0.00, that - # generates three entries. We are looking at the last entry here, - # and it's zero. - - return 0 - - if not self.transfer(participant.username, tip['tippee'], \ - tip['amount'], pachinko=pachinko): - - # The transfer failed due to a lack of funds for the participant. - # Don't try any further transfers. - - log("FAILURE: %s" % msg) - return -1 - - log("SUCCESS: %s" % msg) - return 1 - - - def transfer(self, tipper, tippee, amount, pachinko=False): - """Given two unicodes, a Decimal, and a boolean, return a boolean. - - If the tipper doesn't have enough in their Gittip account then we - return False. Otherwise we decrement tipper's balance and increment - tippee's *pending* balance by amount. - - """ - typecheck( tipper, unicode - , tippee, unicode - , amount, Decimal - , pachinko, bool - ) - with self.db.get_cursor() as cursor: - - try: - self.debit_participant(cursor, tipper, amount) - except NegativeBalance: - return False - - self.credit_participant(cursor, tippee, amount) - context = 'take' if pachinko else 'tip' - self.record_transfer(cursor, tipper, tippee, amount, context) - - return True - - - def debit_participant(self, cursor, participant, amount): - """Decrement the tipper's balance. - """ - - DECREMENT = """\ - - UPDATE participants - SET balance = (balance - %(amount)s) - WHERE username = %(participant)s - AND balance >= %(amount)s - RETURNING pending - - """ - args = dict(amount=amount, participant=participant) - r = cursor.one(DECREMENT, args, default=False) - if r is False: - raise NegativeBalance - assert r is not None, (amount, participant) # sanity check - - - def credit_participant(self, cursor, participant, amount): - """Increment the tippee's *pending* balance. - - The pending balance will clear to the balance proper when Payday is - done. - - """ - - INCREMENT = """\ - - UPDATE participants - SET pending=(pending + %s) - WHERE username=%s - AND pending IS NOT NULL - RETURNING pending - - """ - cursor.execute(INCREMENT, (amount, participant)) - rec = cursor.fetchone() - assert rec is not None, (participant, amount) # sanity check - # Record-keeping. # =============== - def record_transfer(self, cursor, tipper, tippee, amount, context): - cursor.run("""\ - - INSERT INTO transfers - (tipper, tippee, amount, context) - VALUES (%s, %s, %s, %s) - - """, (tipper, tippee, amount, context)) - - - def mark_missing_funding(self): - self.db.one("""\ - - UPDATE paydays - SET ncc_missing = ncc_missing + 1 - WHERE ts_end='1970-01-01T00:00:00+00'::timestamptz - RETURNING id - - """, default=NoPayday) - - - def mark_charge_failed(self): - self.db.one("""\ + @staticmethod + def mark_charge_failed(cursor): + cursor.one("""\ UPDATE paydays SET ncc_failing = ncc_failing + 1 From 9a437933206957dcae256d57a96b930fa4899524 Mon Sep 17 00:00:00 2001 From: Changaco Date: Tue, 24 Jun 2014 14:55:33 +0200 Subject: [PATCH 21/97] after a crash don't rerun the stages that were successfully completed --- gittip/billing/payday.py | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/gittip/billing/payday.py b/gittip/billing/payday.py index da0947b8f2..89702a8f3f 100644 --- a/gittip/billing/payday.py +++ b/gittip/billing/payday.py @@ -87,10 +87,16 @@ def run(self): _start = aspen.utils.utcnow() log("Greetings, program! It's PAYDAY!!!!") - self.payin() - self.payout() - self.update_stats() - self.update_receiving_amounts() + if self.stage < 1: + self.payin() + self.mark_stage_done() + if self.stage < 2: + self.payout() + self.mark_stage_done() + if self.stage < 3: + self.update_stats() + self.update_receiving_amounts() + self.mark_stage_done() self.end() @@ -568,3 +574,14 @@ def mark_ach_failed(self): RETURNING id """, default=NoPayday) + + + def mark_stage_done(self): + self.db.one("""\ + + UPDATE paydays + SET stage = stage + 1 + WHERE ts_end='1970-01-01T00:00:00+00'::timestamptz + RETURNING id + + """, default=NoPayday) From 064fae3a73f78eb248f77273d7652adba5f84105 Mon Sep 17 00:00:00 2001 From: Changaco Date: Tue, 24 Jun 2014 15:12:31 +0200 Subject: [PATCH 22/97] add some log messages --- gittip/billing/payday.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/gittip/billing/payday.py b/gittip/billing/payday.py index 89702a8f3f..92bb177279 100644 --- a/gittip/billing/payday.py +++ b/gittip/billing/payday.py @@ -517,6 +517,7 @@ def update_stats(self): WHERE ts_end='1970-01-01T00:00:00+00'::timestamptz """, {'ts_start': self.ts_start}) + log("Updated payday stats.") def update_receiving_amounts(self): @@ -538,6 +539,8 @@ def update_receiving_amounts(self): """ with self.db.get_cursor() as cursor: cursor.execute(UPDATE) + log("Updated receiving amounts.") + def end(self): self.ts_end = self.db.one("""\ From cca5cd2bfbc8bdae4dfedfc694432a996c0fbcdc Mon Sep 17 00:00:00 2001 From: Changaco Date: Tue, 24 Jun 2014 16:28:58 +0200 Subject: [PATCH 23/97] run a DB check at the end of the payout loop --- gittip/billing/payday.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gittip/billing/payday.py b/gittip/billing/payday.py index 92bb177279..dcc10bda0f 100644 --- a/gittip/billing/payday.py +++ b/gittip/billing/payday.py @@ -100,8 +100,6 @@ def run(self): self.end() - self.db.self_check() - _end = aspen.utils.utcnow() _delta = _end - _start fmt_past = "Script ran for {age} (%s)." % _delta @@ -456,6 +454,8 @@ def payout(self): if participant.is_suspicious is None: log("UNREVIEWED: %s" % participant.username) log("Did payout for %d participants." % i) + self.db.self_check() + log("Checked the DB.") def update_stats(self): From 34d5d005e9971f43d65ab0937cedd830ab018829 Mon Sep 17 00:00:00 2001 From: Changaco Date: Thu, 26 Jun 2014 11:14:47 +0200 Subject: [PATCH 24/97] drop the `pending` column --- branch.sql | 1 + 1 file changed, 1 insertion(+) diff --git a/branch.sql b/branch.sql index 399291bd05..dbbfe4e047 100644 --- a/branch.sql +++ b/branch.sql @@ -1,3 +1,4 @@ BEGIN; ALTER TABLE paydays ADD COLUMN stage integer DEFAULT 0; + ALTER TABLE participants DROP COLUMN pending; END; From 91f7bb8a9ca24146147d2990a63fb90925ede59b Mon Sep 17 00:00:00 2001 From: Changaco Date: Thu, 26 Jun 2014 11:18:01 +0200 Subject: [PATCH 25/97] update tests --- gittip/billing/exchanges.py | 4 +- tests/py/test_billing_exchanges.py | 154 ++++++++------- tests/py/test_billing_payday.py | 289 +++++------------------------ tests/py/test_charts_json.py | 2 +- 4 files changed, 133 insertions(+), 316 deletions(-) diff --git a/gittip/billing/exchanges.py b/gittip/billing/exchanges.py index 6f68da7bba..1e7a69ffd3 100644 --- a/gittip/billing/exchanges.py +++ b/gittip/billing/exchanges.py @@ -294,4 +294,6 @@ def record_exchange(db, kind, amount, fee, error, participant): raise NegativeBalance if hasattr(participant, 'set_attributes'): - participant.set_attributes(balance=balance) + attrs = dict(balance=balance) + attrs['last_%s_result' % kind] = error or '' + participant.set_attributes(**attrs) diff --git a/tests/py/test_billing_exchanges.py b/tests/py/test_billing_exchanges.py index 91364cecf9..2ad47b2cc7 100644 --- a/tests/py/test_billing_exchanges.py +++ b/tests/py/test_billing_exchanges.py @@ -9,8 +9,8 @@ from aspen.utils import typecheck from gittip.billing.exchanges import ( _prep_hit, - charge, - charge_on_balanced, + capture_card_hold, + create_card_hold, record_exchange, skim_credit, ) @@ -20,69 +20,89 @@ from gittip.testing.balanced import BalancedHarness -class TestCharge(BalancedHarness): +class Foobar(Exception): pass - def test_charge_without_cc_details_raises_NoBalancedCustomerHref(self): - alice = self.make_participant('alice') - with self.assertRaises(NoBalancedCustomerHref): - charge(self.db, alice, D('1.00')) - - @mock.patch('gittip.billing.exchanges.charge_on_balanced') - def test_charge_failure_returns_error(self, cob): - cob.return_value = (D('10.00'), D('0.68'), 'FAILED') - actual = charge(self.db, self.janet, D('1.00')) - assert actual == 'FAILED' - - @mock.patch('gittip.billing.exchanges.charge_on_balanced') - def test_charge_success_returns_empty_string(self, charge_on_balanced): - charge_on_balanced.return_value = (D('10.00'), D('0.68'), "") - actual = charge(self.db, self.janet, D('1.00')) - assert actual == '' - - @mock.patch('gittip.billing.exchanges.charge_on_balanced') - def test_charge_success_updates_participant(self, cob): - cob.return_value = (D('10.00'), D('0.68'), "") - charge(self.db, self.janet, D('1.00')) - - janet = Participant.from_username('janet') - expected = {'balance': D('9.32'), 'last_bill_result': ''} - actual = {'balance': janet.balance, - 'last_bill_result': janet.last_bill_result} - assert actual == expected - - -class TestChargeOnBalanced(BalancedHarness): +def raise_foobar(*a): + raise Foobar - def test_charge_on_balanced(self): - actual = charge_on_balanced( 'janet' - , self.janet_href - , D('10.00') # $10.00 USD - ) - assert actual == (D('10.61'), D('0.61'), '') - def test_charge_on_balanced_small_amount(self): - actual = charge_on_balanced( 'janet' - , self.janet_href - , D('0.06') # $0.06 USD - ) - assert actual == (D('10.00'), D('0.59'), '') +class TestCardHolds(BalancedHarness): - def test_charge_on_balanced_failure(self): - customer_with_bad_card = self.make_balanced_customer() + def test_create_card_hold_without_cc_details_raises_NoBalancedCustomerHref(self): + alice = self.make_participant('alice') + with self.assertRaises(NoBalancedCustomerHref): + create_card_hold(self.db, alice, D('1.00')) + + @mock.patch('balanced.Customer') + def test_create_card_hold_failure(self, Customer): + Customer.fetch = raise_foobar + hold, error = create_card_hold(self.db, self.janet, D('1.00')) + assert hold is None + assert error == "Foobar()" + + def test_create_card_hold_success(self): + hold, error = create_card_hold(self.db, self.janet, D('1.00')) + janet = Participant.from_id(self.janet.id) + assert isinstance(hold, balanced.CardHold) + assert hold.failure_reason is None + assert hold.amount == 1000 + assert error == '' + assert self.janet.balance == janet.balance == 0 + + def test_capture_card_hold_full_amount(self): + hold, error = create_card_hold(self.db, self.janet, D('20.00')) + assert error == '' # sanity check + assert hold.meta['state'] == 'new' + + capture_card_hold(self.db, self.janet, D('20.00'), hold) + janet = Participant.from_id(self.janet.id) + assert self.janet.balance == janet.balance == D('20.00') + assert self.janet.last_bill_result == janet.last_bill_result == '' + assert hold.meta['state'] == 'captured' + + def test_capture_card_hold_partial_amount(self): + hold, error = create_card_hold(self.db, self.janet, D('20.00')) + assert error == '' # sanity check + + capture_card_hold(self.db, self.janet, D('15.00'), hold) + janet = Participant.from_id(self.janet.id) + assert self.janet.balance == janet.balance == D('15.00') + assert self.janet.last_bill_result == janet.last_bill_result == '' + + def test_capture_card_hold_too_high_amount(self): + hold, error = create_card_hold(self.db, self.janet, D('20.00')) + assert error == '' # sanity check + + with self.assertRaises(balanced.exc.HTTPError): + capture_card_hold(self.db, self.janet, D('25.00'), hold) + + janet = Participant.from_id(self.janet.id) + assert self.janet.balance == janet.balance == 0 + + def test_capture_card_hold_amount_under_minimum(self): + hold, error = create_card_hold(self.db, self.janet, D('20.00')) + assert error == '' # sanity check + + capture_card_hold(self.db, self.janet, D('0.01'), hold) + janet = Participant.from_id(self.janet.id) + assert self.janet.balance == janet.balance == D('9.41') + assert self.janet.last_bill_result == janet.last_bill_result == '' + + def test_create_card_hold_bad_card(self): + customer_href = self.make_balanced_customer() card = balanced.Card( number='4444444444444448', expiration_year=2020, expiration_month=12 ).save() - card.associate_to_customer(customer_with_bad_card) + card.associate_to_customer(customer_href) - actual = charge_on_balanced( 'whatever username' - , customer_with_bad_card - , D('10.00') - ) - assert actual == (D('10.61'), D('0.61'), '402 Client Error: PAYMENT REQUIRED') + bob = self.make_participant('bob', balanced_customer_href=customer_href, + is_suspicious=False) + hold, error = create_card_hold(self.db, bob, D('10.00')) + assert error == '402 Client Error: PAYMENT REQUIRED' - def test_charge_on_balanced_handles_MultipleFoundError(self): + def test_create_card_hold_multiple_cards(self): customer_href = self.make_balanced_customer() card = balanced.Card( number='4242424242424242', @@ -98,19 +118,17 @@ def test_charge_on_balanced_handles_MultipleFoundError(self): ).save() card.associate_to_customer(customer_href) - actual = charge_on_balanced( 'whatever username' - , customer_href - , D('10.00') - ) - assert actual == (D('10.61'), D('0.61'), 'MultipleResultsFound()') + bob = self.make_participant('bob', balanced_customer_href=customer_href, + is_suspicious=False) + hold, error = create_card_hold(self.db, bob, D('10.00')) + assert error == 'MultipleResultsFound()' - def test_charge_on_balanced_handles_NotFoundError(self): - customer_with_no_card = self.make_balanced_customer() - actual = charge_on_balanced( 'whatever username' - , customer_with_no_card - , D('10.00') - ) - assert actual == (D('10.61'), D('0.61'), 'NoResultFound()') + def test_create_card_hold_no_card(self): + customer_href = self.make_balanced_customer() + bob = self.make_participant('bob', balanced_customer_href=customer_href, + is_suspicious=False) + hold, error = create_card_hold(self.db, bob, D('10.00')) + assert error == 'NoResultFound()' class TestFees(Harness): @@ -130,14 +148,14 @@ def prep(self, amount): def test_prep_hit_basically_works(self): actual = _prep_hit(D('20.00')) expected = (2091, - u'Charging %s 2091 cents ($20.00 + $0.91 fee = $20.91) on %s ' u'... ', + u'2091 cents ($20.00 + $0.91 fee = $20.91)', D('20.91'), D('0.91')) assert actual == expected def test_prep_hit_full_in_rounded_case(self): actual = _prep_hit(D('5.00')) expected = (1000, - u'Charging %s 1000 cents ($9.41 [rounded up from $5.00] + ' u'$0.59 fee = $10.00) on %s ... ', + u'1000 cents ($9.41 [rounded up from $5.00] + $0.59 fee = $10.00)', D('10.00'), D('0.59')) assert actual == expected diff --git a/tests/py/test_billing_payday.py b/tests/py/test_billing_payday.py index d72121ab21..ae31c60213 100644 --- a/tests/py/test_billing_payday.py +++ b/tests/py/test_billing_payday.py @@ -4,11 +4,8 @@ import balanced import mock -from psycopg2 import IntegrityError -from aspen.utils import utcnow from gittip.billing.payday import Payday -from gittip.exceptions import NegativeBalance from gittip.models.participant import Participant from gittip.testing import Harness from gittip.testing.balanced import BalancedHarness @@ -27,9 +24,7 @@ def fetch_payday(self): class TestPayday(BalancedHarness): - @mock.patch('gittip.billing.exchanges.charge_on_balanced') - def test_payday_moves_money(self, charge_on_balanced): - charge_on_balanced.return_value = (D('10.00'), D('0.68'), "") + def test_payday_moves_money(self): self.janet.set_tip_to(self.homer, '6.00') # under $10! Payday.start().run() @@ -37,11 +32,9 @@ def test_payday_moves_money(self, charge_on_balanced): homer = Participant.from_username('homer') assert homer.balance == D('6.00') - assert janet.balance == D('3.32') + assert janet.balance == D('3.41') - @mock.patch('gittip.billing.exchanges.charge_on_balanced') - def test_payday_doesnt_move_money_from_a_suspicious_account(self, charge_on_balanced): - charge_on_balanced.return_value = (D('10.00'), D('0.68'), "") + def test_payday_doesnt_move_money_from_a_suspicious_account(self): self.db.run(""" UPDATE participants SET is_suspicious = true @@ -56,9 +49,7 @@ def test_payday_doesnt_move_money_from_a_suspicious_account(self, charge_on_bala assert janet.balance == D('0.00') assert homer.balance == D('0.00') - @mock.patch('gittip.billing.exchanges.charge_on_balanced') - def test_payday_doesnt_move_money_to_a_suspicious_account(self, charge_on_balanced): - charge_on_balanced.return_value = (D('10.00'), D('0.68'), "") + def test_payday_doesnt_move_money_to_a_suspicious_account(self): self.db.run(""" UPDATE participants SET is_suspicious = true @@ -86,33 +77,23 @@ def test_payday_moves_money_with_balanced(self): janet_customer = balanced.Customer.fetch(janet.balanced_customer_href) homer_customer = balanced.Customer.fetch(homer.balanced_customer_href) - homer_credits = homer_customer.credits.all() - assert len(homer_credits) >= 1 - assert homer_credits[0].amount == 1500 - assert homer_credits[0].description == 'homer' + credit = homer_customer.credits.first() + assert credit.amount == 1500 + assert credit.description == 'homer' - janet_debits = janet_customer.debits.all() - assert len(janet_debits) >= 1 - assert janet_debits[0].amount == 1576 # base amount + fee - assert janet_debits[0].description == 'janet' + debit = janet_customer.debits.first() + assert debit.amount == 1576 # base amount + fee + assert debit.description == 'janet' class TestBillingCharges(PaydayHarness): - def test_mark_missing_funding(self): - before = self.fetch_payday() - missing_count = before['ncc_missing'] - - self.payday.mark_missing_funding() - - after = self.fetch_payday() - assert after['ncc_missing'] == missing_count + 1 - def test_mark_charge_failed(self): before = self.fetch_payday() fail_count = before['ncc_failing'] - self.payday.mark_charge_failed() + with self.db.get_cursor() as cursor: + self.payday.mark_charge_failed(cursor) after = self.fetch_payday() assert after['ncc_failing'] == fail_count + 1 @@ -120,19 +101,6 @@ def test_mark_charge_failed(self): class TestBillingPayday(PaydayHarness): - def test_move_pending_to_balance_for_teams_does_so(self): - self.make_participant('A', number='plural', balance=2, pending=3) - self.payday.move_pending_to_balance_for_teams() - actual = self.db.one("SELECT balance FROM participants WHERE username='A'") - assert actual == 5 - - def test_move_pending_to_balance_for_teams_ignores_new_teams(self): - # See https://github.com/gittip/www.gittip.com/issues/1684 - self.make_participant('A', number='plural', balance=0, pending=None) - self.payday.move_pending_to_balance_for_teams() - actual = self.db.one("SELECT balance FROM participants WHERE username='A'") - assert actual == 0 - def test_update_receiving_amounts_updates_receiving_amounts(self): A = self.make_participant('A') B = self.make_participant('B', claimed_time='now', last_bill_result='') @@ -154,58 +122,18 @@ def test_update_receiving_amounts_includes_taking(self): assert Participant.from_username('A').receiving == 13 assert Participant.from_username('A').taking == 3 - @mock.patch('gittip.billing.payday.Payday.transfer') @mock.patch('gittip.billing.payday.log') - def test_tip(self, log, transfer): - self.db.run(""" - UPDATE participants - SET balance=1 - WHERE username='janet' - """) - amount = D('1.00') - invalid_amount = D('0.00') - tip = { 'amount': amount - , 'tippee': 'janet' - , 'claimed_time': utcnow() - } - - result = self.payday.tip(self.janet, tip) - assert result == 1 - result = transfer.called_with('janet', tip['tippee'], tip['amount']) - assert result - - assert log.called_with('SUCCESS: $1 from mjallday to alice.') - - # XXX: Should these tests be broken down to a separate class with the - # common setup factored in to a setUp method. - - # XXX: We should have constants to compare the values to - # invalid amount - tip['amount'] = invalid_amount - result = self.payday.tip(self.janet, tip) - assert result == 0 - - tip['amount'] = amount - - # XXX: We should have constants to compare the values to - # transfer failed - transfer.return_value = False - result = self.payday.tip(self.janet, tip) - assert result == -1 - - @mock.patch('gittip.billing.payday.log') - def test_start_prepare_and_zero_out(self, log): + def test_start_prepare(self, log): self.clear_tables() - self.make_participant('bob', balance=10, claimed_time=None, pending=1) - self.make_participant('carl', balance=10, claimed_time='now', pending=1) + self.make_participant('bob', balance=10, claimed_time=None) + self.make_participant('carl', balance=10, claimed_time='now') payday = Payday.start() ts_start = payday.ts_start get_participants = lambda: self.db.all("SELECT * FROM pay_participants") - payday.prepare() - payday.zero_out_pending() + payday.prepare(self.db, ts_start) participants = get_participants() @@ -213,7 +141,6 @@ def test_start_prepare_and_zero_out(self, log): ('Starting a new payday.'), ('Payday started at {}.'.format(ts_start)), ('Prepared the DB.'), - ('Zeroed out the pending column.'), ] expected_logging_call_args.reverse() for args, _ in log.call_args_list: @@ -224,8 +151,7 @@ def test_start_prepare_and_zero_out(self, log): # run a second time, we should see it pick up the existing payday payday = Payday.start() second_ts_start = payday.ts_start - payday.prepare() - payday.zero_out_pending() + payday.prepare(self.db, second_ts_start) second_participants = get_participants() assert ts_start == second_ts_start @@ -240,7 +166,7 @@ def test_start_prepare_and_zero_out(self, log): ('Picking up with an existing payday.'), ('Payday started at {}.'.format(second_ts_start)), ('Prepared the DB.'), - ('Zeroed out the pending column.')] + ] expected_logging_call_args.reverse() for args, _ in log.call_args_list: assert args[0] == expected_logging_call_args.pop() @@ -269,164 +195,35 @@ def test_payday(self, end, payin, log): assert end.call_count == 1 -class TestBillingTransfer(PaydayHarness): - def setUp(self): - PaydayHarness.setUp(self) - self.tipper = self.make_participant('lgtest') - - def test_transfer(self): - amount = D('1.00') - sender = self.make_participant('test_transfer_sender', pending=0, - balance=1) - recipient = self.make_participant('test_transfer_recipient', pending=0, - balance=1) - - result = self.payday.transfer( sender.username - , recipient.username - , amount - ) - assert result == True - - # no balance remaining for a second transfer - result = self.payday.transfer( sender.username - , recipient.username - , amount - ) - assert result == False - - def test_debit_participant(self): - amount = D('1.00') - subject = self.make_participant('test_debit_participant', pending=0, - balance=1) - - initial_amount = subject.balance - - with self.db.get_cursor() as cursor: - self.payday.debit_participant(cursor, subject.username, amount) - - subject = Participant.from_username('test_debit_participant') - - expected = initial_amount - amount - actual = subject.balance - assert actual == expected - - # this will fail because not enough balance - with self.db.get_cursor() as cursor: - with self.assertRaises(NegativeBalance): - self.payday.debit_participant(cursor, subject.username, amount) - - def test_credit_participant(self): - amount = D('1.00') - subject = self.make_participant('test_credit_participant', pending=0, - balance=1) - - initial_amount = subject.pending - - with self.db.get_cursor() as cursor: - self.payday.credit_participant(cursor, subject.username, amount) - - subject = Participant.from_username('test_credit_participant') # reload - - expected = initial_amount + amount - actual = subject.pending - assert actual == expected - - def test_record_transfer(self): - amount = D('1.00') - subjects = ['jim', 'kate', 'bob'] - - for subject in subjects: - self.make_participant(subject, balance=1, pending=0) - - with self.db.get_cursor() as cursor: - # Tip 'jim' twice - for recipient in ['jim'] + subjects: - self.payday.record_transfer( cursor - , self.tipper.username - , recipient - , amount - , 'tip' - ) - - for subject in subjects: - # 'jim' is tipped twice - expected = amount * 2 if subject == 'jim' else amount - actual = self.db.one( "SELECT sum(amount) FROM transfers " - "WHERE tippee=%s" - , (subject,) - ) - assert actual == expected - - def test_record_transfer_invalid_participant(self): - amount = D('1.00') - - with self.db.get_cursor() as cursor: - with self.assertRaises(IntegrityError): - self.payday.record_transfer( cursor - , 'idontexist' - , 'nori' - , amount - , 'tip' - ) - - class TestPachinko(Harness): - def test_pachinko_pachinkos(self): - a_team = self.make_participant('a_team', claimed_time='now', number='plural', balance=20, \ - pending=0) - a_team.add_member(self.make_participant('alice', claimed_time='now', balance=0, pending=0)) - a_team.add_member(self.make_participant('bob', claimed_time='now', balance=0, pending=0)) - - payday = Payday.start() - payday.prepare() - payday.pachinko() - - assert Participant.from_username('alice').pending == D('0.01') - assert Participant.from_username('bob').pending == D('0.01') - - def test_pachinko_sees_current_take(self): - a_team = self.make_participant('a_team', claimed_time='now', number='plural', balance=20, \ - pending=0) - alice = self.make_participant('alice', claimed_time='now', balance=0, pending=0) - a_team.add_member(alice) - a_team.set_take_for(alice, D('1.00'), alice) - - payday = Payday.start() - payday.prepare() - payday.pachinko() - - assert Participant.from_username('alice').pending == D('1.00') - - def test_pachinko_ignores_take_set_after_payday_starts(self): - a_team = self.make_participant('a_team', claimed_time='now', number='plural', balance=20, \ - pending=0) - alice = self.make_participant('alice', claimed_time='now', balance=0, pending=0) + def test_transfer_takes(self): + a_team = self.make_participant('a_team', claimed_time='now', number='plural', balance=20) + alice = self.make_participant('alice', claimed_time='now') a_team.add_member(alice) - a_team.set_take_for(alice, D('0.33'), alice) - - payday = Payday.start() - + a_team.add_member(self.make_participant('bob', claimed_time='now')) a_team.set_take_for(alice, D('1.00'), alice) - payday.prepare() - payday.pachinko() - - assert Participant.from_username('alice').pending == D('0.33') - - def test_pachinko_ignores_take_thats_already_been_processed(self): - a_team = self.make_participant('a_team', claimed_time='now', number='plural', balance=20, \ - pending=0) - alice = self.make_participant('alice', claimed_time='now', balance=0, pending=0) - a_team.add_member(alice) - a_team.set_take_for(alice, D('0.33'), alice) - payday = Payday.start() - a_team.set_take_for(alice, D('1.00'), alice) - - for i in range(4): - payday.prepare() - payday.pachinko() - - assert Participant.from_username('alice').pending == D('0.33') + # Test that payday ignores takes set after it started + a_team.set_take_for(alice, D('2.00'), alice) + + # Run the transfer multiple times to make sure we ignore takes that + # have already been processed + for i in range(3): + payday.prepare(self.db, payday.ts_start) + payday.transfer_takes(self.db, payday.ts_start) + payday.update_balances(self.db) + + participants = self.db.all("SELECT username, balance FROM participants") + + for p in participants: + if p.username == 'a_team': + assert p.balance == D('18.99') + elif p.username == 'alice': + assert p.balance == D('1.00') + elif p.username == 'bob': + assert p.balance == D('0.01') + else: + assert p.balance == 0 diff --git a/tests/py/test_charts_json.py b/tests/py/test_charts_json.py index 8f8f163d94..9ca0ef1633 100644 --- a/tests/py/test_charts_json.py +++ b/tests/py/test_charts_json.py @@ -9,7 +9,7 @@ def today(): return datetime.datetime.utcnow().date().strftime('%Y-%m-%d') -class Tests(Harness): +class TestChartsJson(Harness): def setUp(self): Harness.setUp(self) From 336d9d62ac1d99015a746f680cbe810fc7f1f338 Mon Sep 17 00:00:00 2001 From: Changaco Date: Thu, 26 Jun 2014 22:12:36 +0200 Subject: [PATCH 26/97] improve test coverage of gittip.billing.exchanges --- tests/py/test_billing_exchanges.py | 51 ++++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/tests/py/test_billing_exchanges.py b/tests/py/test_billing_exchanges.py index 2ad47b2cc7..ac54fc7d04 100644 --- a/tests/py/test_billing_exchanges.py +++ b/tests/py/test_billing_exchanges.py @@ -9,12 +9,14 @@ from aspen.utils import typecheck from gittip.billing.exchanges import ( _prep_hit, + ach_credit, + cancel_card_hold, capture_card_hold, create_card_hold, record_exchange, skim_credit, ) -from gittip.exceptions import NegativeBalance, NoBalancedCustomerHref +from gittip.exceptions import NegativeBalance, NoBalancedCustomerHref, NotWhitelisted from gittip.models.participant import Participant from gittip.testing import Harness from gittip.testing.balanced import BalancedHarness @@ -26,6 +28,38 @@ def raise_foobar(*a): raise Foobar +class TestCredits(BalancedHarness): + + def test_ach_credit_withhold(self): + bob = self.make_participant('bob', last_ach_result="failure", balance=20, + balanced_customer_href=self.homer_href, + is_suspicious=False) + withhold = D('1.00') + error = ach_credit(self.db, bob, withhold) + assert error == '' + bob2 = Participant.from_id(bob.id) + assert bob.balance == bob2.balance == 1 + + def test_ach_credit_amount_under_minimum(self): + bob = self.make_participant('bob', last_ach_result="failure", balance=8, + balanced_customer_href=self.homer_href, + is_suspicious=False) + r = ach_credit(self.db, bob, 0) + assert r is None + + @mock.patch('balanced.Customer') + def test_ach_credit_failure(self, Customer): + Customer.fetch = raise_foobar + bob = self.make_participant('bob', last_ach_result="failure", balance=20, + balanced_customer_href=self.homer_href, + is_suspicious=False) + + error = ach_credit(self.db, bob, D('1.00')) + bob2 = Participant.from_id(bob.id) + assert bob.last_ach_result == bob2.last_ach_result == error == "Foobar()" + assert bob.balance == bob2.balance == 20 + + class TestCardHolds(BalancedHarness): def test_create_card_hold_without_cc_details_raises_NoBalancedCustomerHref(self): @@ -33,6 +67,12 @@ def test_create_card_hold_without_cc_details_raises_NoBalancedCustomerHref(self) with self.assertRaises(NoBalancedCustomerHref): create_card_hold(self.db, alice, D('1.00')) + def test_create_card_hold_for_suspicious_raises_NotWhitelisted(self): + bob = self.make_participant('bob', is_suspicious=True, + balanced_customer_href='fake_href') + with self.assertRaises(NotWhitelisted): + create_card_hold(self.db, bob, D('1.00')) + @mock.patch('balanced.Customer') def test_create_card_hold_failure(self, Customer): Customer.fetch = raise_foobar @@ -46,9 +86,13 @@ def test_create_card_hold_success(self): assert isinstance(hold, balanced.CardHold) assert hold.failure_reason is None assert hold.amount == 1000 + assert hold.meta['state'] == 'new' assert error == '' assert self.janet.balance == janet.balance == 0 + # Clean up + cancel_card_hold(hold) + def test_capture_card_hold_full_amount(self): hold, error = create_card_hold(self.db, self.janet, D('20.00')) assert error == '' # sanity check @@ -74,11 +118,14 @@ def test_capture_card_hold_too_high_amount(self): assert error == '' # sanity check with self.assertRaises(balanced.exc.HTTPError): - capture_card_hold(self.db, self.janet, D('25.00'), hold) + capture_card_hold(self.db, self.janet, D('20.01'), hold) janet = Participant.from_id(self.janet.id) assert self.janet.balance == janet.balance == 0 + # Clean up + cancel_card_hold(hold) + def test_capture_card_hold_amount_under_minimum(self): hold, error = create_card_hold(self.db, self.janet, D('20.00')) assert error == '' # sanity check From 2936f1accf1378fae14bf24c8c17b7cc6007a21b Mon Sep 17 00:00:00 2001 From: Changaco Date: Mon, 14 Jul 2014 18:21:18 +0200 Subject: [PATCH 27/97] remove PaydayHarness class --- gittip/testing/__init__.py | 4 ++++ tests/py/test_billing_payday.py | 42 +++++++-------------------------- 2 files changed, 12 insertions(+), 34 deletions(-) diff --git a/gittip/testing/__init__.py b/gittip/testing/__init__.py index ea6e852a08..44a48e0067 100644 --- a/gittip/testing/__init__.py +++ b/gittip/testing/__init__.py @@ -196,3 +196,7 @@ def make_payday(self, *transfers): active.add(t) cursor.run("INSERT INTO paydays (ts_start, ts_end, nactive, transfer_volume) VALUES (%s, %s, %s, %s)", (ts_start, ts_end, len(active), transfer_volume)) + + + def fetch_payday(self): + return self.db.one("SELECT * FROM paydays", back_as=dict) diff --git a/tests/py/test_billing_payday.py b/tests/py/test_billing_payday.py index ae31c60213..265ba36492 100644 --- a/tests/py/test_billing_payday.py +++ b/tests/py/test_billing_payday.py @@ -11,17 +11,6 @@ from gittip.testing.balanced import BalancedHarness -class PaydayHarness(BalancedHarness): - - def setUp(self): - BalancedHarness.setUp(self) - self.alice = self.make_participant('alice', claimed_time='now') - self.payday = Payday.start() - - def fetch_payday(self): - return self.db.one("SELECT * FROM paydays", back_as=dict) - - class TestPayday(BalancedHarness): def test_payday_moves_money(self): @@ -85,21 +74,13 @@ def test_payday_moves_money_with_balanced(self): assert debit.amount == 1576 # base amount + fee assert debit.description == 'janet' - -class TestBillingCharges(PaydayHarness): - def test_mark_charge_failed(self): + payday = Payday.start() before = self.fetch_payday() - fail_count = before['ncc_failing'] - with self.db.get_cursor() as cursor: - self.payday.mark_charge_failed(cursor) - + payday.mark_charge_failed(cursor) after = self.fetch_payday() - assert after['ncc_failing'] == fail_count + 1 - - -class TestBillingPayday(PaydayHarness): + assert after['ncc_failing'] == before['ncc_failing'] + 1 def test_update_receiving_amounts_updates_receiving_amounts(self): A = self.make_participant('A') @@ -107,7 +88,7 @@ def test_update_receiving_amounts_updates_receiving_amounts(self): B.set_tip_to(A, D('10.00'), update_tippee=False) assert Participant.from_username('A').receiving == 0 - self.payday.update_receiving_amounts() + Payday.start().update_receiving_amounts() assert Participant.from_username('A').receiving == 10 def test_update_receiving_amounts_includes_taking(self): @@ -118,7 +99,7 @@ def test_update_receiving_amounts_includes_taking(self): assert Participant.from_username('A').receiving == 0 assert Participant.from_username('A').taking == 3 - self.payday.update_receiving_amounts() + Payday.start().update_receiving_amounts() assert Participant.from_username('A').receiving == 13 assert Participant.from_username('A').taking == 3 @@ -171,13 +152,8 @@ def test_start_prepare(self, log): for args, _ in log.call_args_list: assert args[0] == expected_logging_call_args.pop() - @mock.patch('gittip.billing.payday.log') - def test_end(self, log): - self.payday.end() - assert log.called_with('Finished payday.') - - # finishing the payday will set the ts_end date on this payday record - # to now, so this will not return any result + def test_end(self): + Payday.start().end() result = self.db.one("SELECT count(*) FROM paydays " "WHERE ts_end > '1970-01-01'") assert result == 1 @@ -187,9 +163,7 @@ def test_end(self, log): @mock.patch('gittip.billing.payday.Payday.end') def test_payday(self, end, payin, log): greeting = 'Greetings, program! It\'s PAYDAY!!!!' - - self.payday.run() - + Payday.start().run() assert log.called_with(greeting) assert payin.call_count == 1 assert end.call_count == 1 From 9ca940cd58456fca6ad264bd8f1f349632f74887 Mon Sep 17 00:00:00 2001 From: Changaco Date: Tue, 15 Jul 2014 11:55:37 +0200 Subject: [PATCH 28/97] remove a log message --- gittip/billing/payday.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/gittip/billing/payday.py b/gittip/billing/payday.py index dcc10bda0f..c258678f4a 100644 --- a/gittip/billing/payday.py +++ b/gittip/billing/payday.py @@ -441,8 +441,6 @@ def payout(self): SELECT p.*::participants FROM participants p WHERE balance > 0 """) for i, participant in enumerate(participants, start=1): - if i % 100 == 0: - log("Payout done for %d participants." % i) withhold = participant.giving + participant.pledging try: error = ach_credit(self.db, participant, withhold) From f6e0868aa2c2fdac43e056621c3e8c388c47b7bb Mon Sep 17 00:00:00 2001 From: Changaco Date: Tue, 15 Jul 2014 11:56:08 +0200 Subject: [PATCH 29/97] trick the coverage measurement --- gittip/billing/payday.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/gittip/billing/payday.py b/gittip/billing/payday.py index c258678f4a..22fd98cc8e 100644 --- a/gittip/billing/payday.py +++ b/gittip/billing/payday.py @@ -26,8 +26,7 @@ class NoPayday(Exception): - def __str__(self): - return "No payday found where one was expected." + __str__ = lambda self: "No payday found where one was expected." class Payday(object): From 623f594c070d542ac5063903f9f3bb2a457e4046 Mon Sep 17 00:00:00 2001 From: Changaco Date: Tue, 15 Jul 2014 17:05:17 +0200 Subject: [PATCH 30/97] upgrade balanced --- requirements.txt | 2 +- vendor/balanced-1.0.1beta2.tar.gz | Bin 8691 -> 0 bytes vendor/balanced-1.1.1.tar.gz | Bin 0 -> 9275 bytes 3 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 vendor/balanced-1.0.1beta2.tar.gz create mode 100644 vendor/balanced-1.1.1.tar.gz diff --git a/requirements.txt b/requirements.txt index 3a6a86fa6a..8b3e78208a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -28,7 +28,7 @@ ./vendor/iso8601-0.1.4.tar.gz ./vendor/mock-0.8.0.tar.gz -./vendor/balanced-1.0.1beta2.tar.gz +./vendor/balanced-1.1.1.tar.gz ./vendor/python-bitcoinaddress-0.2.2.tar.gz ./vendor/raven-3.1.4.tar.gz diff --git a/vendor/balanced-1.0.1beta2.tar.gz b/vendor/balanced-1.0.1beta2.tar.gz deleted file mode 100644 index f8239be72f9b46fffcefd3464c329781d08121c2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8691 zcmV*N%l0o7?)Zc%S=elM7 zb6(7*ZZH~u!TH}2^W8fC__;d&J3E^&{~N3M|J|R92~2tD7yhLc@;u0*X%VH#`Y4Jy z8?twY`>%dJbSI%@UGVYPi;_`#X%+t1yPidH2sP*nw8z>T^d-Mbyo}#PIl!Tp);Fu? zpGy9J-akHieRy{6M#-Ha2!b<+1;Q3ja z|Mg#Bcwn*j*uWiF$Nq%xF~u8}QKp#0I-5-wMxz@g58L-S=$` zt+!FYlN`YRaQ4c2Rr)b|?0E@`$wz-UN#ZEs?B^0yJ(yBY~SDCyR9=0E%F!X9nbCn{9mBLcm}->#ZX((J4llvi>_xyl#H7d1}=M@X16RV z^s7ym9b_Cm)Ok9~*a$wtd@(Tz%()sz1q*I?aGTBwwtmef^Yw(^ugiK@CB3&?_SY;@ zaDo&};o2GwdzJtQw2JxKny$U=vcn|Do{-{H%JL$D&r?6oq2DBhbr!xhx_Ifbi_^o? z?=(+L=hMiQtb<&E-(O7m^t)b(g>JnABTXk0Kt`0^06BTChA2wb{Ry-8!;s}5lZqI& zfx@oKPG|WID^dpfuu7&W7Ql3mqokWm-)VY7IW&P0qq6`bZBCatv9Bu3lY>C}S(LU& z;-L=+sd5%acQ8gk2(i_zSP0HZ0IWGD#*A_t|1^to9

qbw_C&r}w}`d(AG^LDh^R zm{vf=o%pvYya7#llZHltQCkjBRXtP4Ri(9ciU zHJ8<)R17Ab<1kMJGq4|d!ZROBujkM_33=w};RATr=vOk3#I8!{f=^I!bDVJKGf07H zCjzS0JSe5L9@iK5X`M=Au*cpX|8#uv*JJA-2ELC*5eNN#loZ%HWc#x);?M`%+hecz z9gou~*qTaRsoSx}$=MOxpH4GS7$JTKZLf+eO@OZB%m;o?#_X-sDOg|7u2S_pokjtc zou#AV-Uo4&Xds2~{o4pcAd7&q|?yT(pQ#{X}J+qWmRPJpMN9g><*obI2$ z@y<`Y*GF#;`^;9-V#8<8L%r^KASkw2{YrRVhMQs8Gk48XU3eb6u^o5qFFpGi)g7hS z)s!a=b|%_uO5u|Mx>*8$(9f7gF?UMveSCe^1I1kgH_j+^!48H_ujh`lbT)Mc0vy;K zF!=^Zs2Idx?VX-Ug*Z*dp0W4$5#-PuAqnub5?iwn zkhr*%2xW}iJTQ!emu?2wXLe7b;pWpgDjfT7iQOyFh(<_eLF3GJ{+6#j@BJVC`Ew8E zAHf=x@c|nNUfzs%K!BF({BQZU?TsrND*hCZ4IO0c*|pKsU3<>Q;~_9o@6P}R3I+;V z8Gzw&v)@r?zvu-TO_cyGLW`hSR)tH;T9r8qIktcm{@jCBx6OI0SNL7#4k>WKN0*&M zsAJJO0VM>xB)Un3Iuy$?<;9RC@Yir0j_1*syq@*T;k&b=lVhk;)lAmglj9$~SBGZ@ z?~YE-jT%j4%py#$3!ZTQ1Fo%<1-D?N^5I2Ei%U_2OCB$u!TM=v;}Qr^ zieqy5WfyWWsnkwBY|%n?eH+y7^;_9`*dY#`00JGO0^~KX+Digw5_PKjxVV|wVsY5a ztob=nGYJQ#p|@HVe3kxxohN_q{Ev;7o7MQ==El~>=F0znis#-BhQkeabJL3Q^t+vn z0e%~}+tv(2OCkh-uXa|l`fnru>YWR|<~pYQz4(0j|ET=m&5g~?omKwt*E9cTC-2`K z9G-omF<7SmUk-L^^S?P5toDDNCjY0*^|ZgsRT%=PkM(6iScuHTZ z{C{3X;78~Gp&z)C|5g6qD*tbl|F_EjTjl?)^8Z%(f2;hzKhXbcbCthf{BL7W^Z&Lt zcUS)3lRT@i-YWjLT>d|Ak-Jp>2X*=1#A++~f5Q1+>4BB}cgw#S1N%Mp{~GeYv$@*; z`?~i3R-*E6Bmb?D{mP zE4Sd^*#3X1i@-kq{x5O=%WD7s51xO$UHkd-zv%qO=GL}8|FN^POZi``_|MZkqb!{; z&l}A!itl;q=mi)6j9q&%IDwfT=N8q&d%lWj!u0_S`Q1|IJ#zRP^Ivn^{mB{r6%D0t zsoKe}sulR4mZ+yVPgUd(fhcg*8{q4t{mY1F#cWaF_wCRmy> z)Wt^_wxh$=ILX7~G~s2gYnG;khe^_^ud)bw(l?zA?SVTmQe+X5=Z~TDG7Veq;JvWf zx8^tufB~L|@rwDB4{d)sjiZ2q^e^s`(1qTG$AhDTb{^ubZg)i?R2{Mgqsj3;3zC0w-xnQ_HOAd^++M(6<%5d|eY{o0bW^XdpGChrMh zA*^-J^iA|kA63s9NENCm{65ExreH$5XEHZ_#5`3MaEtsn`UiK?>hTR22kusb+&F1p zYnsNP89I28x}Z@cSM`7^?fZsf@(l9X#%AOkS+dM#l4Ln&iBGqDZ`Tr5d`h52Uh0_5v;Nr(p}hYaC1Lct{5y@z~C@_enmR zqGdzPb?>ew6#( zm)-$*IGDl`kTd| z^vZ_VC%UU(q!V;Rd?9R1aCGjnH|aeGKJ3>(!5HS$SY$TdXLxQp#5~g|gpnjrmihDA zDft)=c`K21l{&bf(Og;$;%h0oPD9Fo(`Yvd{~Txb9<$hHahTWzupH1~LW!zCZt|-r z{STaaF*d9Xs-5{Qu*R7176rOP*`&|FVvKnKVElq|YQY6_+kD#wk2$0X)Yy9+uIzI} z3aN@SiOd21Rmw1>Q$WH0aalq`B2-$zNGyfeG-@&-E*JN>#=1bfF6L;g^@d{+j zth`C%P_P}6ri(17(r^6S&>bWc3#e1oG*Tg4T=rTUjGL8v>ZSuRR7th($^6xqg-Y)d z4>#2`UI3#$A1Ecy#H9|%Gg6nd)>l_QR8+U*4GLT}m=DY}a*)VI>}0t+AJ!bOI7irq z=i=)34!rHvl$v>T!43Q@^lSc)dsY@wby9yjJ@Nf<=fgS783+1EW ztuZDntHHgrmI{SufD`}?fZ_-6Ks&pz*dDtq0NasHc;-f7LziNAhLyM|hjH1aOh*wc z#RX4|z%RM>gIi=~rN5N2^&6VD1r`y95H#rPruU#?J%zH>6n=%DB5uQOxWKkfg>3y! zX<7DY#oSaLC;|MT$b4{IH1`M>28rt6fBll*Iz$%d;0`B1SL_yTuis>syDrA6()!K* z!B6E;n{ia!%&zIU#c4WA!c%`72)VeB8sL(q_#=7}grio~f^M4imX4k*Oalq z5;bwZ2C4TEUP}Xh8$sxt4_S6SWW!E_aQZA)-f^p;hNgZp?`B2=i!!cRTSc`h1?5Xs z2|$)jtPNW{u}=3Fv2_5zoqYO=lssTNII8^r!U=2T3^UDM_x=Joj^ zAFbX+YlL8dYgw@_eG|i8tKgd3P8B-zOEel`P-$#i!Wg*Rbkw@D&)P!^%@Brgb!st; z$~|kXFAS8bo**r&&0vK~uAx=cMRZl)zEY*eZGF|*x=FJv3PHz!pk4F8$Hf&mCrQBq z=|YI*<|NATlB$-4lNpVnmY!~DeFQqsVbNbi0pDw`S)qH{)iGAUaz!E*H!bhfx3Y;M?9FhB7?-&M^*($pP;1@$fTL1;6k+h$eVX0+8F~d2 ze5ip$&6KC{9U_n81x^I5G;!Ja4PpVF1H1QgHcfL38nggtaY4GywJJ~KKepPoA}%gl z#7DVnl^9@`7#YGYA`or5LS7{e(%7hUO1DTW5zN7a)+JE1pzS{@DYx}uUUMD3^3dRW z9uQ+XHeS>c3BBZ05;)Iu*47Y+E4J;bpdF;&|aH1?Xtr8h}t~=vmd|FmAwb3_6%my8X&E8G5fEmsbtI$IJ(BB};F4Myd^O zJwU&eG9mD~2pt#F*G=!A9=TQvbmRrG5CcndRSgkcfr}LdHw_H%*f=fW2;TJ!TJtkVBY2~wty0k#{$Y6~X48{qOO*_KJ z?}%B6xY{V`30Kaj(P@KvW(YEvWf@8`%r$xWGm4-BhmtF-E?#KG!w?#%mCaqyuS)(V zbI-(JNMq6xB^awqnEnSjR#ZV>oG`-eh#AIn3(OwkVkH4oLj_2CK+9zb;j#%j_R=@Q zHHGNqngblVxkQ(>u=m>9@yYq&-r5>FM!sQV;9!Q=q5!q{1Gq%;?h^r2_UF&p>B-r- zxG+SrB2U6;8UfKuKM^kj(c*?-WgOqD!?%a$hqyRlxIuze*sV}XF()QBV(DVW)U}W) z<(Cge0>Ocwg>RrEr>^W(H)XVcaYsfy>`H&FsPq75CT?|JqTIVnBlJsOgKY9vbx%u- z3b-leXy}b{nHbto@uUTuhO0c3HUw1!c}mqVxlSe4#!@37iFcm)N$!h~N|%vhPc<^3LZMd%?4kpy}qDJU+Ts__N0`f zP}D_c&%g*ZI=nEtVB{upDQAo5eF=gsj5|?%9^EIVi|N=CqxZkmVC+syjUjG zj3Ijv#v63I5h9Vzq-9rZI+r?Cs?7=N#?p$VCFS3BDPM2EA6oQ|mS%k(~#JN=CQHSaexl^d#Zqd73DQ3VDn`R>z4o^pb+A z!jer2$P{A{H|&rnVHy^TldOZ%L?3Iyg!AxNSrT(bJ{XPNeUva^tL3sJReifCiH@s+ zM3SR{)kTNE0k?=wT15zfE*2o7O{e$}QlBI|67nMH5ml<LOJ{pkz0%1t>~CLqZ8hc&VQt!nCle0H%f!LszojYL5cd4t|$Jx0kHk zMJ2Ir$Iqresnz0Q>Pt#Lj{eHFXmi~*1G4SsO@Zp^_nrggWOdAfv>eOkK~)noA->Vf-N?xl%`hqCD24bRW_&?;hBS1dR5a-YoMEN>=T zU4<#86jM0#is^0oRwi;AhKz>0$c}y5;!==(D5?Kg1E&|hVrBD*Gbpv_vgJ*4X=Aw7 zZS&pY28gb#ZtBT3S_k>$TIGw;Pb$VA{N#3DjLK<@qM;j&+Nc##6>3e`r@)P0(}r@P zucJYxqbehk*i0=4am|>B6h^rlEp-)(maX`%0XI)h&=Px5t%^3od{c2-!IY>Y8%vpq z>dMo7%D@>$*d$(_GGV;Sa}3f%2q1V7ZD|)AAv?Vmt}=lBVsyVqN;{f#F`h{p&m5xQ z#YeGLrJoMV`_6EHXwMV#CXgY-6g-}qkE7&L9;%Mju#dFmlT9ir#~bKRoAq#>s)76W z@?&d-&9cJg!kC{CuY82eV)(J@T`JFIRFh3<&nm`@Q#V~f7ASf|EVK-RRzwR!qRaKR zQjRJ(+oc|L%=Jra$OGj8b$X1dMwQBHmkGTzx&MT2c$73PE!!6^w}PXbdg0JFH6W~h zXTc4|6o|Z}GLdzaeG;QFghlw?6IXYU%M&DH;P05x4})SC`uG(k>EkgO{|huk`(s?aQCUjR8boDID!*vyAY?{`Sd4rW`X7OK%WWYt zb8>-dQwKYU(WkTPI11u9T}6}U5$1Z~p;U2>2{)`!bIHwZ+V=yt!z2nz-w}Wtm$Z8b z6%X&^>Uym1j_mZfsV}r)a zB*S!v_V(fu&1guwAY5eQm|Dk?$?F-!hxZwW3!S2p({r6wt%j*7*uj>8}eF+%+ ze=5U@>X)|C%n>o!%5@m>2LPq3G@=9AqnsCX!Crj{JrYOGN9JL6QILxQ_)|;I)u5s8 zKD$N4>Y(wcIiU>(5sRt;X;CCo_5n!y1xARHt2!*Z>Q&Q)^Ey7_(AQo)Z(on8?OLc> zp6oMmvK9%T^J1TsNFv(aa`f{$`qZxHn8)PlNCOAUpw?R8)C5iBjf`ikKuPF23ANA6 z+r;X38(@7(!`Jl*M6Z3ap`<-ATwL5}<`g;xs-K5uCyJICrRd;)S<^Dl2{(2uaFR5p z<^^{+Ev?z(>VmuLU%Q&sE?+AfT!;fCmsa()2W_2 z{b#5eO=B(^`ZRm0K&*SmI8`1pe zOq{L3ZuKB^yDH}5q9Qd2c~k-Gdmdg)q>k*!dqXVq{sPZS_&tF zevM;!q|JRy<9b^maRnAER(wR19M`go^TB$sQsrx{M17s4^=c@`&h zd#-M4wyJG@(~pyS^y8F66MD&MMwR;Ij*4h)?G(GW+{Yphv0*VCE)iGi;^O+^w-1-S zi;c_f8(L~tW%cot+=BBhbQ%a4J;xs@6_eXe!cQKNAP+Q^nod{GFP4$BWF;a1o)-l8 z7m6I0K!|2Hg3IW@r+8=gWnmpBHLHylRA4WUX)eRRXK0H&sP+_=KdNij51Ps8z|g=R zv)y0QC~-j4oV#9m;mL*Qr*DphBEi8WO7dO9d6hsHF+)Y2$~*RI_vkiKS71mB=SAkS z$P=SgRe-z=pDt`XpfOIfxgh*yztq*eT0+cT3t(NjX*Ys0(4xB)$uga3PUgnzYd?zd zh(L)>i*fAOv{h{%(}4uL|Ni`qcYJd0ogN;)Iy(MQ?XRxOO{#U{KnpZp2Sd7!mrVh9 zy9Drh|LE=Et7UCPz|Uy*>iy~4ql5kP%bKQ=bEwlMy)v-@U zRx8PAQ5opBcyWC}KQB&~yj`phE_>GH%4V&e)w6n5&+1t{t7rABp4GE@R?q5LJ*#K+ Rte#JM{vT&gTs{EE002G|G#&r| diff --git a/vendor/balanced-1.1.1.tar.gz b/vendor/balanced-1.1.1.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..a1a875f03b2158219611aacc46bfab7000337bd8 GIT binary patch literal 9275 zcmV-BB*fbviwFoM1));{|72-%bT49IY+-I=Wn?WeE-@}KE_7jX0PQ_%bK6Fe{)}IN zwXX`GLP4UQIbHEKiY+I)IFibeck528f*@i@Ap!v&08--m{NJy;=YbhKNZN6f>@Jat zMGj_qW_tQPJ;S>o4w3;2H+vrZxAo$;c(&m=+~4Pa5BT5hojv}SKQDSayZhTaP`U^2 zd%L}z!x#4ci)VPIc@bo`{o;-#3+q9(;c&?$aU$~XKge@uuD|@c_4}^>-oe54di{UT z_4oZKi3;EMCiAbd{s((|ZR_7V+}Y>r-#a|)^#FyvgTupv7xwmg{eSakn5ARe_lMJB znlazEqwyro3VWJFgEVA*92G1J;@q-)f5x&rN)xEmw;duE$Fiusq#AFKCc|hXf$(nt z*Q_O9XDm;r*?{F0AgWwebS)d6_oBFK{~TlwteC_BemTB4{fW)HRONM$g+Hco*tK70 zER2e-{f6D)pZ9DyO+x&OlL%s7{VEHRJQx%(sjmHfkUSg@2I(|``UnUbNBLx0FbO-3 z?}EWY*FKpstV4e)rSj{$bi`X}Q2)ZRXc!Fw-rZ}UyL8O5u6>?`@P}%jJOQ?oAQmmG zPBY@z)4;+7fzD_#uBkCcgy;K7Fou<^_22$RA6ccAoKUSe3mb|M_tj!rh-#sje5IHc zt6Ij_pi)Z6ZYpaTZ*_4huQh_IyrIRZlu1IY6(fz+3OT`5iZ#lsj?^T9%GP>>Ih!BkHcuwbfvvlu`M=-WUCaMJ zlKiK7VNkFl8Z%i-ezz=9ki*}Y{gS7N9ppCtrnkifIhW%6cVqk`=OHsh+rg!DpJY ztbdj!LgwR>rD@?$v$!w&DqlfQe#T}I4yTVA(dinH{9pu~_Z`J!lX-EUCR>jEjXnQ3#M`5I`=F9 z36?uu`{SpMMYV=$WYw)Wefwm|BrIUFby3AovsFk zPXz&d-2POy`Plp9crbu*P`KJ`yC$Fx0TPocCw8r7bED6(oQs%XG_)O-WohOQ;@q*5 zw7^Mn7;2Dp91Tdo~UCb0&BOsVe7X~-U;6q0>6m&uJFXx@Jct{fr zw8`ODKqxYt7f#2M+{Kcv#Bd)Qd37X~eF@Ce*w^7*EnFKgDsl$E71+>at&0P^8%Beo zvM?lLkn5O-8U$E0cPmu{s0WWFVUAdL9X}|XjvXYS(ZTh@O&2sn&cOPKl2HQ~e6Si| zOsE!5%mK)T0Mrn}ES(mss{t;^Bu(Ng;~La{`SCr>E) z&}@78A<3r`RBfR3blD4n^ON&Z5>;)=%f@0f&}a~*fz+&;i!K}4ieyC-MaF3m=57^6 z4ab~F8>oqh9VMF5z#SAa5f@QH&UCC7>*sfi|DYcHD#rf`_W$7_#ea4W54U^!hZO(W z-d)Fk{#fxJ@dB;vIJ&b8+g%t4-wWd5idM-uX(o`Y^+AaHK8aaPxCTX$Mhg)jd@7} zJ4*{5j6rL~SRJjM@QX_G*JV$lhR_`ML5?$ra!)D%STvOAj<%pu%RX7!w;!*rE@-SC zQk^fGyBixCFprc2cB`hL+L<%%BJp{0^4t=o`o0eqO(0f`>@oSiB*TO~!aJ;orhnJQ zRTWkS8Z-}8g9a)AeIhJxmB!V!9=6O?tWJMM00t3CkRCd<_sWzCBdXMCwxS|5BUt9v z?57HR!;~^W-82r`FH%kk-gH9d)Ze3tt7g=-ui^8Jy+Ks|4w)2|i!M1j5R~v2z9UMm80lO(0iowPxORMT zn^E}1Gverhrb6bc>iP9Dbe5z<2td~m;9pC0+yEl#-Eb_}=djM>gQL?1*P*Sw#uK9?@_bAy~b ztLY>GbP+Ka;*@A%W253x7TMgSH<;4Gb)h;>IQhCLK-z{+*A6&3Y?Nkmj(G6Xl&mhN z!AR8{H2_wX>w06rr)zWzEk1s#Dz{URbJ-N5eEK%V+aQY3uPBGppdMScKxErhIuKxw zKV1FjpPgU%7bjmHx1$pyv6w@dy0Vg9czL8uQuVnf)}vZ{@>f}^?H^4-@_fKzPA7W zfcF2Cl-DE%zF#SP-xblpu01UxgkxoF1k{(!fml(mOS}2KD0Z`SS}=c-v0?Poceb3; z4#b$-<=~!;gV$*sGul3Lbr@x_H2A)oWGPq>#azkCdjMQ@KlFG7Oi*>mql3|K{&Vyc z@veNc%nBmN`aPz8=ElUE}IrfS&Cv!zF_JQcP+~ z50V)uWE_Ac3j}}>Qv04|%nsoHUqC9^_n`Z-F?ei&mMWnSOKhEJ`4WkmeCzmL*_Y7Y zGzOtpHpD*BpJq8|3*g^?Gn>sRI``}!(?{IN?p8p-9Ol$G2j-6L$1DIJ1lT{xBQVf{ z1m0!AyvdVNo9I#;T+?iBtQzta7u}`d9H*#;T}S*YT-iqkGG`ixiBkZ{0UpLt!U)|I zSH)QWeUyv2VeM%SV{L#nVhJE|pgI&yx;AJGuz&zefGn67pj5pkudGI6+%}VQ@3e%n zU*Vt-tGJR#9BERdHZ-w06#N&rC1^@CltwUOl$>p9HR`-vYX^gMTl`UjA&eDT%o);M zWwes6kY(MLnqqSaB;=GeR*;Owhup$y260G~Mu^e?pi7O>w0;5;f8r=?TP&93pgTD= zz{1@JGiE@xp{M&a4msH&YTD4E$5;4L`yzVLh8@Zzm`JYrUfRN5wfJQKCe{~-GVmiUN$Z=sA;4is?$z#vmlEw+=Z|; z%f(aWE|hjELTy}T1LfWz$U+}iFE@plT3t=elX&Ul_`Dn(=7Mr^jAl3jAJ-td10x5w ztFS=YC={pAU|BWlrM;ApTLy>$wC>S*$s&->p3S_GXAfj@RTS1_DR!r6iR*G2H%-EH z7$u?2X=*5vr;wPX{F1^ps7cxeScDuJpoU*BeFPEf%OP8J=9lA>$gSxO@*v{I1w@xmzHo`J-{p0cLpN?#q z5HpI3`{^B~z-(P$-1s7xmwsN9=U{kl^}gL}g@=l)G7#`HQHVH43sGGgz7vIxwXinV zDV#5^g#@^vCH{<_IO3?4`Jx?Xou#v$fN6*1P2iaL*a+oUX8{lf*q_II9{C!TZNx3WBSPct{uI)#M zYo~H4k$#;|)#A2^qeV>3(7qqiR}7TfTR|QUw?5*(pX|r`XmAf+T#)3gV1j}gmz+Z$ z1tIQ^k(Q2U+)s*%HXA<3$?f!P`;_|#IHYm#K;~y5Ezk+Zi@9yo?hF8n#L*xs;yGG; z*c#d*aQv$rw?@q?GSQS2j&d`vqiwYsE48!Vt3{@C6?S7FXMqP`7y(_K5IT+WRnHDI z$hBJzqZVctHc>;g38Mn83=eee<_J(d(!?#&N~oz8hOZT9Loy9^ihk%ZB?`)Ss&RET zDePmp+!Xt6GVSiuEQ>-=1t1Ua*dRb3m|>Q@JrI^2j|z;V9CtSx{98R}MlF5S()x_y zyaGxqq5(UqhZZu%lkc=Zg>W2s?1V>-8slWjV&)Pd$DCsu6b_O=o`MJfyBYWj!;rv1 z=;Y-o`a8&*HU0O>zU8{_wu>9bCy(yDOH>?ccloMeRbv+|kCUnBkIVM~jDbLjH%RD31jeuTKs?2oR*gzmW zfyPXRD5*JSLFTr-6&W6~2lc`jrjq(4n}sX}@4w1u1Y4|=R>R}kSIYwdYOJS(89AhK zfIN>_Z!1)!N+ZFAY0XvUPE%_taXuXmqbIxBq6J9ez1Vm`V0HEsTUNX?6~VI#Iiq zwyA?De(gEWt*Ll5SIywCN`jtS*Gy&lm`Fhur{CoH2R?DNZ7DyB;}t@R^X<$J?@xbV ziR+Xb=yV1~e*5HT)H?;bOf$BK@&(^4%miY^;GPF%Y29joPy(q{T>uE{G=UL>iTN>E zA-qIwdbRx}v?&?5hGbjb2TzsPYBvSlZ;+{XIYl`{oDqz&{9f+fP@p2hgT!No#tj7^ z!49kLXS|ZTFxWg{VB~<}y9Mt>9_c=x$M73;6np z@_l8BD4vJ*tOn>Pb$FNu45V8#0DUWh1Jw~gPdNpM2;SWkTC+Tt;9)H9%dJ#AlFNM( zej=OuQGkea5Cv#}(+Q)%ICR`P&cw!Sv-Hsx89i!z0~XD&H|QIW9p}8qH)V*9Vi?#n zs;g@6bgVh&4njU1%UFr%%e{>fpMSmuTIKLu4%^-o2c2_JHu7{cmh#+UAON+nDuD|% zs=le>EEJqZO_ZHlffAqJQ8tN31XgpraTkIzWt%sr-V^l_a+NKm7F_yJTBkMa>0y?^ zG|NzDz*>`YI;H&?piuJQM4qA7I1WubLoMJ^FbkxlAJ2V#1bv#5;wVm7ZOruFNU^*M z%2*l^hMLM;FvcBVz#6Jr+F@SKO9@%brdju3r;u)~Tp1_i4oY}-h2E4yuZeN^VUf~3kGtH84l153Pxi<-WEZGe=t#E#C zPTrkdouE%`S0&v zt6@A0aAo2~n>)(=SsI}`b(Luw=ek`%j=a$*Z!HxRT?e`Ub=LTq@SM zy$Y9;&u;1ZoGBt90xo$-pbN%GV& z;uf|L8!d9WC1v~|DR5pz*PMY`F2t|4VD*)1S`&(n*K%+LI)%0&1;<4| zXy45#b}1$J?SSgs%HRUtNzeqZ5e>ZUGCmcRh)LL54K#|ksyI;_e|e9g!Nkv4g7T!rC3k~(SencfA`oh2b6UWo^Y@Uw1CDzUg2`vTLe;a}<&rLLP+K(zgJOCWptt=B*rSuLv| zG{>@akk$A~@NfEB)P!DpAfqlHapy6@?M&MR;eMAE_Z<}GBBeOPqhH{520R5tESM)f zIg&BLQ4`ycx7bKoH>T&-tf4qkq)rpUF-0&f_Lx|W7PJm|7k;GrkO)Ret>^~NZOOoj z5Zv7KZi55gm zj+5q6?ev-yMy}Md$)lvR7F!uBS{kH7CP5aA(R*M)miuYq3mQ)D#VMSASZoJ)@OzQF#f3Qjq`+_U+@6%4TO;^z11s@CU2dD*mg$vlSI-;wuwmkI{n)TzD zHPNfpMxl$6RtefJg@uBOuoZCx6>HM%U=i=cX42b4mo(w&*CItjh(bGB=1}6XOkb@3 z5_M!&MU|wN7*_ENPo&PPL7-+@ooT06rT)}(ypFs)wYhCjp1~3thy%erlv3@uoHaWQ zMB8%0c99ThY0^e`I%<4?{l^YSGXYcou`3rtvBbqhWW$3p}JgSK9*Orj-D&2O2RMs%DEcdX`2o$?pXyr#q zU6I?z#-+W=XW;%qT>^r0JssR*$b!&IA`?khsy{v(jaj&9KlW4?xj6kc0{V{aQI<|8 z*o8VeN(YJzDEOL&qd7~w=NbvNg(#%Ow~S;|H6k1F%?!L4aCCRj@ZPEkU-+WG!_xAmTgA0wWx16cDgjV_%WF0_J8*6?UqI5*j&7# zq7E)6eXOn}^bM|zyr;3WA65|zT6JL~xztBrWkRiTT4{lVXnwS7Lk&-bTpXEBEbXGD zlMvYi+}HW6;eW=!8*Vxk>BI{}n>>_8gg%+x#nB+1(>1zz9$^9`9;oMMFmXE^C70Mb zr(H!LJ4^<$)HOM{ElPWnP?7EoN62&x7-_Us&xo|=Wx7UI(hp%A^rJq8~N550Vk1%Y2wpe9AMt!@MOCTmy|tfqZ^} zOA9ekCjAmTi#mv9`dGv9GR}E#*jqdkhA02ic;{@Zv%4Ij+!4W)nOVCZW{%hg<*1L^ynKJT`I@LEwzT#a*|HcinMEPr ztHgnd#$9_i3q?*l22`LLO0YCrV*JW@LPY5RJ_%?#c92b3+x$hBEBd1K>O^&wQY66j zRjU6~_rLP+=-0UccIof`92~yr?d%-x?Cc%x9qi)$uX{Va_5DwOsQaJPCGha?g^2vV z5m(`*najiG=2fr8{f?ks_i%gP0R@0uD~@1g-qa@=dad}F;S@8&+TUzM<=b)-I=Cox`Crk}Txlc`s}{?qmM(}+Hz#O{YILC^E({F>agRTwZ~@?QLw-2lMI`-E3QwVUn2IXhT~w;8?9t91uHzloWvxw!*oF5Iv)BD z3PpO4kB^PN4Y&m@@~w2nvKfH?GgKH&q4z#VM&t1C=a$GNlL20%0wDfvnmy>Z1MPSs z8H4Yz4&fu7PvsP0ySF1u62SYZ@JQ6=ojsm!ChT!j)VnPgf7i4BiWd-w!;yFu{>BDQ z8=UsSF7RG^ZFTlM`-ERmi5RE1;>aTrp-ULwQMhH&WF7t{P+=6xDy zn&1nbZrYBfZxz1raM)x@Dc%%dt}mCqux||F$w9gVS(G-2g_8gVB;VUHoyQb+)2LP= z=|0Wo;%7N<{*x@?$B@MZRdJj?0u>$AyVyj}9K$HMe>e^vQYiVEA7?3FXtm{eLs9io z4$fGfO>j^_a8aHYtSNlI;PGY_8UO%)L4t=6cy;ZR0#5US6NCMO+#SQ1c80D(=Bw!g zcu(tB5Re$1iD-Ep94d`N+4#~iAXO~|E{9yb^FXIy}JHO?-Xaaa0Wx<$E zSiTc71?zL%8C9>+Ni@KBm+7#0#O*0D3`+kz!tQ|uvIuBQ0kD5g!)Xjk_P4bEq-yyB zzi(y#Z@=pQZ6EHffB*Mcp2uL&?{9lMyO#LnMEutC_AKp&Ml3p5yGs9>{@>;Khxe~f zE`J64FXjLJ-a(cB;oF-3pT++>K0AGTa(U%NiS_>E_|4BJ-Z->`(C79sth@y#;&kE- zh9mK5GPlrX+^khp4u#zQ!K(FNmA5KCG-);d@QhX4j!f1Y*y*Ky#r{s;co)WO&5V;TSR-~ZY_OwfjG~|G&4hyXOD( z{?B^CSFMMjbJ$1#Ge-_z)^sz#30YSdWDh_QYTqs6KkDVAoG{Ay!lH6!G+jE^rfI)0V0 z@x?~Q0^@=Z-)2NuU*Bl3Rx=Wo7zfX76g;Ofu#WPqtpBTFy=RI4Ztw4){@*zO{l9l` z$j|?+_kW+|Svmh>%|-tT{IB-;pV9tDbH5t@J={B7^Z#=^*JBX?zOlkGc)B@^V*05m zwJT&@vk~6-IZSV?A{hC1> zy+g|X+CJRd**n-dp#9&2oi+bI$AcGyC_h((mBi1W#T0P@``Nl=%)Y%^Y`1B+w?x6`f4e-hPC5;mI;`r)E|LWX- zd;0FAi#IR|7al%q32qLLItzFp{qRTPUV0n8?83)$`i|>u{JG=st7C>Kb~Ry%i=B0> z+EvKn#5T+goj(xAZVA4N#g`rMo$#mMhN%axXy|r29^LBZ_Bgm9UdA13Afe(Q28Y}2 z=vcr@+k8#-e+D`fMF<3}Iw>{c6~{?G`0VSxiHiz~?jaVV)nDSzvE5km$2%#~0=_AY zOErVhXHG{9!^M$IUU*@161qs((>txHxA6+pa32V%^KSqKUi2iia^r)3O1;wWu(e+d z#8~aW@7**kf@0i?pw}|iteRt4TeoV3-(>9&1J`VL(~5*zDrnOJln|T}>6UTXp9#gX zbbirzDU33lhwJ+^C%dY4>G>JdF(s4r?)>Zr|INwe>-VP@S6Yoa*IGO^mtR4O z;QN}m(&bZMbk-FYKgE4#;nuup9rF#2zVrR%8&MR{7Cn@h{qSO+cJBL5+s?hHtj(eC zEKewqgQ-lvU&T{b$gsf!D5<=EUE<<~zrv`apHIQb?K>ESlt0p&W}*ub&y`{)?>9&x zry4JDI^9OH?zgZ*D}aI88-l?rT6LCeSUKe#i_U8MReFoX+f{naU$a{!;J`AhFAe*{ z+5e5XeZP79Z*RMD|JUIzxCCqa{}~<>eHQ++i5oG=%o?YI2MafGkQ8>-9J+qi&-z(E d>u3F}pY^kT*3bG`KmR1p{{x+j>>&Wi000OM72^N^ literal 0 HcmV?d00001 From 3b5ed71a6a0af2b610f7b7ede00c65aa8ce82137 Mon Sep 17 00:00:00 2001 From: Changaco Date: Tue, 15 Jul 2014 18:10:02 +0200 Subject: [PATCH 31/97] 100% test coverage for gittip.billing.payday --- gittip/testing/__init__.py | 7 ++ tests/py/test_billing_exchanges.py | 8 +- tests/py/test_billing_payday.py | 135 ++++++++++++++++++++++++++++- 3 files changed, 139 insertions(+), 11 deletions(-) diff --git a/gittip/testing/__init__.py b/gittip/testing/__init__.py index 44a48e0067..8c2a0ae7fa 100644 --- a/gittip/testing/__init__.py +++ b/gittip/testing/__init__.py @@ -200,3 +200,10 @@ def make_payday(self, *transfers): def fetch_payday(self): return self.db.one("SELECT * FROM paydays", back_as=dict) + + +class Foobar(Exception): pass + + +def raise_foobar(*a): + raise Foobar diff --git a/tests/py/test_billing_exchanges.py b/tests/py/test_billing_exchanges.py index ac54fc7d04..d4e07b6483 100644 --- a/tests/py/test_billing_exchanges.py +++ b/tests/py/test_billing_exchanges.py @@ -18,16 +18,10 @@ ) from gittip.exceptions import NegativeBalance, NoBalancedCustomerHref, NotWhitelisted from gittip.models.participant import Participant -from gittip.testing import Harness +from gittip.testing import Harness, raise_foobar from gittip.testing.balanced import BalancedHarness -class Foobar(Exception): pass - -def raise_foobar(*a): - raise Foobar - - class TestCredits(BalancedHarness): def test_ach_credit_withhold(self): diff --git a/tests/py/test_billing_payday.py b/tests/py/test_billing_payday.py index 265ba36492..258cf8efeb 100644 --- a/tests/py/test_billing_payday.py +++ b/tests/py/test_billing_payday.py @@ -5,9 +5,11 @@ import balanced import mock -from gittip.billing.payday import Payday +from gittip.billing.exchanges import create_card_hold +from gittip.billing.payday import NoPayday, Payday +from gittip.exceptions import NegativeBalance from gittip.models.participant import Participant -from gittip.testing import Harness +from gittip.testing import Harness, raise_foobar from gittip.testing.balanced import BalancedHarness @@ -158,18 +160,126 @@ def test_end(self): "WHERE ts_end > '1970-01-01'") assert result == 1 + def test_end_raises_NoPayday(self): + with self.assertRaises(NoPayday): + Payday().end() + @mock.patch('gittip.billing.payday.log') @mock.patch('gittip.billing.payday.Payday.payin') @mock.patch('gittip.billing.payday.Payday.end') def test_payday(self, end, payin, log): greeting = 'Greetings, program! It\'s PAYDAY!!!!' Payday.start().run() - assert log.called_with(greeting) + log.assert_any_call(greeting) assert payin.call_count == 1 assert end.call_count == 1 -class TestPachinko(Harness): +class TestPayin(BalancedHarness): + + def create_card_holds(self): + payday = Payday.start() + with self.db.get_cursor() as cursor: + payday.prepare(cursor, payday.ts_start) + return payday.create_card_holds(cursor) + + @mock.patch.object(Payday, 'fetch_card_holds') + @mock.patch('gittip.billing.payday.create_card_hold') + def test_hold_amount_includes_negative_balance(self, cch, fch): + self.db.run(""" + UPDATE participants SET balance = -10 WHERE username='janet' + """) + self.janet.set_tip_to(self.homer, 25) + fch.return_value = {} + cch.return_value = (None, 'some error') + self.create_card_holds() + assert cch.call_args[0][-1] == 35 + + def test_payin_fetches_and_uses_existing_holds(self): + self.janet.set_tip_to(self.homer, 20) + hold, error = create_card_hold(self.db, self.janet, D(20)) + assert hold is not None + assert not error + with mock.patch('gittip.billing.payday.create_card_hold') as cch: + cch.return_value = (None, None) + self.create_card_holds() + assert not cch.called, cch.call_args_list + + @mock.patch.object(Payday, 'fetch_card_holds') + def test_payin_cancels_existing_holds_of_insufficient_amounts(self, fch): + self.janet.set_tip_to(self.homer, 30) + hold, error = create_card_hold(self.db, self.janet, D(10)) + assert not error + fch.return_value = {self.janet.id: hold} + with mock.patch('gittip.billing.payday.create_card_hold') as cch: + fake_hold = object() + cch.return_value = (fake_hold, None) + holds = self.create_card_holds() + assert len(holds) == 1 + assert holds[self.janet.id] is fake_hold + assert hold.voided_at is not None + + @mock.patch('gittip.billing.payday.CardHold') + @mock.patch('gittip.billing.payday.cancel_card_hold') + def test_fetch_card_holds_handles_extra_holds(self, cancel, CardHold): + fake_hold = mock.MagicMock() + fake_hold.meta = {'participant_id': 0} + fake_hold.save = mock.MagicMock() + CardHold.query.filter.return_value = [fake_hold] + for attr, state in (('failure_reason', 'failed'), + ('voided_at', 'cancelled'), + ('debit_href', 'captured')): + holds = Payday.fetch_card_holds(set()) + assert fake_hold.meta['state'] == state + fake_hold.save.assert_called_with() + assert len(holds) == 0 + setattr(fake_hold, attr, None) + holds = Payday.fetch_card_holds(set()) + cancel.assert_called_with(fake_hold) + assert len(holds) == 0 + + @mock.patch('gittip.billing.payday.log') + def test_payin_cancels_uncaptured_holds(self, log): + self.janet.set_tip_to(self.homer, 42) + alice = self.make_participant('alice', claimed_time='now', + is_suspicious=False, balance=50) + self.db.run(""" + INSERT INTO exchanges + (amount, fee, participant) + VALUES (50, 0, 'alice'); + """) + alice.set_tip_to(self.janet, 50) + Payday.start().payin() + assert log.call_args_list[-3][0] == ("Captured 0 card holds.",) + assert log.call_args_list[-2][0] == ("Canceled 1 card holds.",) + assert Participant.from_id(alice.id).balance == 0 + assert Participant.from_id(self.janet.id).balance == 8 + assert Participant.from_id(self.homer.id).balance == 42 + + def test_payin_cant_make_balances_more_negative(self): + self.db.run(""" + UPDATE participants SET balance = -10 WHERE username='janet' + """) + payday = Payday.start() + with self.db.get_cursor() as cursor: + payday.prepare(cursor, payday.ts_start) + cursor.run(""" + UPDATE pay_participants + SET new_balance = -50 + WHERE username IN ('janet', 'homer') + """) + with self.assertRaises(NegativeBalance): + payday.update_balances(cursor) + + @mock.patch.object(Payday, 'fetch_card_holds') + @mock.patch('balanced.Customer') + def test_card_hold_error(self, Customer, fch): + self.janet.set_tip_to(self.homer, 17) + Customer.fetch = raise_foobar + fch.return_value = {} + Payday.start().payin() + payday = self.fetch_payday() + assert payday['ncc_failing'] == 1 def test_transfer_takes(self): a_team = self.make_participant('a_team', claimed_time='now', number='plural', balance=20) @@ -201,3 +311,20 @@ def test_transfer_takes(self): assert p.balance == D('0.01') else: assert p.balance == 0 + + +class TestPayout(Harness): + + def test_payout_no_balanced_href(self): + self.make_participant('alice', claimed_time='now', is_suspicious=False, + balance=20) + Payday.start().payout() + + @mock.patch('gittip.billing.payday.ach_credit') + def test_payout_ach_error(self, ach_credit): + self.make_participant('alice', claimed_time='now', is_suspicious=False, + balance=20) + ach_credit.return_value = 'some error' + Payday.start().payout() + payday = self.fetch_payday() + assert payday['nach_failing'] == 1 From be2ffee1561dcd0b7843ef88df36b207bfe08e30 Mon Sep 17 00:00:00 2001 From: Changaco Date: Wed, 16 Jul 2014 11:26:44 +0200 Subject: [PATCH 32/97] reduce the number of Balanced API requests --- tests/py/test_billing_payday.py | 16 ++++++++++++---- tests/py/test_charts_json.py | 6 +++++- tests/py/test_history.py | 6 +++++- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/tests/py/test_billing_payday.py b/tests/py/test_billing_payday.py index 258cf8efeb..73c553b2f0 100644 --- a/tests/py/test_billing_payday.py +++ b/tests/py/test_billing_payday.py @@ -15,8 +15,10 @@ class TestPayday(BalancedHarness): - def test_payday_moves_money(self): + @mock.patch.object(Payday, 'fetch_card_holds') + def test_payday_moves_money(self, fch): self.janet.set_tip_to(self.homer, '6.00') # under $10! + fch.return_value = {} Payday.start().run() janet = Participant.from_username('janet') @@ -25,13 +27,15 @@ def test_payday_moves_money(self): assert homer.balance == D('6.00') assert janet.balance == D('3.41') - def test_payday_doesnt_move_money_from_a_suspicious_account(self): + @mock.patch.object(Payday, 'fetch_card_holds') + def test_payday_doesnt_move_money_from_a_suspicious_account(self, fch): self.db.run(""" UPDATE participants SET is_suspicious = true WHERE username = 'janet' """) self.janet.set_tip_to(self.homer, '6.00') # under $10! + fch.return_value = {} Payday.start().run() janet = Participant.from_username('janet') @@ -40,13 +44,15 @@ def test_payday_doesnt_move_money_from_a_suspicious_account(self): assert janet.balance == D('0.00') assert homer.balance == D('0.00') - def test_payday_doesnt_move_money_to_a_suspicious_account(self): + @mock.patch.object(Payday, 'fetch_card_holds') + def test_payday_doesnt_move_money_to_a_suspicious_account(self, fch): self.db.run(""" UPDATE participants SET is_suspicious = true WHERE username = 'homer' """) self.janet.set_tip_to(self.homer, '6.00') # under $10! + fch.return_value = {} Payday.start().run() janet = Participant.from_username('janet') @@ -55,8 +61,10 @@ def test_payday_doesnt_move_money_to_a_suspicious_account(self): assert janet.balance == D('0.00') assert homer.balance == D('0.00') - def test_payday_moves_money_with_balanced(self): + @mock.patch.object(Payday, 'fetch_card_holds') + def test_payday_moves_money_with_balanced(self, fch): self.janet.set_tip_to(self.homer, '15.00') + fch.return_value = {} Payday.start().run() janet = Participant.from_username('janet') diff --git a/tests/py/test_charts_json.py b/tests/py/test_charts_json.py index 9ca0ef1633..33dfb4ad59 100644 --- a/tests/py/test_charts_json.py +++ b/tests/py/test_charts_json.py @@ -3,6 +3,8 @@ import datetime import json +from mock import patch + from gittip.billing.payday import Payday from gittip.testing import Harness @@ -29,7 +31,9 @@ def setUp(self): self.bob.set_tip_to(self.carl, '2.00') def run_payday(self): - Payday.start().run() + with patch.object(Payday, 'fetch_card_holds') as fch: + fch.return_value = {} + Payday.start().run() def test_no_payday_returns_empty_list(self): diff --git a/tests/py/test_history.py b/tests/py/test_history.py index f1dc20791b..4b63022353 100644 --- a/tests/py/test_history.py +++ b/tests/py/test_history.py @@ -2,6 +2,8 @@ from decimal import Decimal +from mock import patch + from gittip.billing.payday import Payday from gittip.models.participant import Participant from gittip.testing import Harness @@ -29,7 +31,9 @@ def test_iter_payday_events(self): assert bob.balance == 0 for i in range(2): - Payday.start().run() + with patch.object(Payday, 'fetch_card_holds') as fch: + fch.return_value = {} + Payday.start().run() self.db.run(""" UPDATE paydays SET ts_start = ts_start - interval '1 week' From 70336b3719b5bd5b1a484ea1d372f9e012d0e2c2 Mon Sep 17 00:00:00 2001 From: Changaco Date: Wed, 16 Jul 2014 12:02:10 +0200 Subject: [PATCH 33/97] fix test --- tests/py/test_billing_payday.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/py/test_billing_payday.py b/tests/py/test_billing_payday.py index 73c553b2f0..b14a8e9cf1 100644 --- a/tests/py/test_billing_payday.py +++ b/tests/py/test_billing_payday.py @@ -76,11 +76,13 @@ def test_payday_moves_money_with_balanced(self, fch): janet_customer = balanced.Customer.fetch(janet.balanced_customer_href) homer_customer = balanced.Customer.fetch(homer.balanced_customer_href) - credit = homer_customer.credits.first() + created_at = balanced.Transaction.f.created_at + + credit = homer_customer.credits.sort(created_at.desc()).first() assert credit.amount == 1500 assert credit.description == 'homer' - debit = janet_customer.debits.first() + debit = janet_customer.debits.sort(created_at.desc()).first() assert debit.amount == 1576 # base amount + fee assert debit.description == 'janet' From 23733eb1a3394228baac13efe8b5a7ab7084f5e8 Mon Sep 17 00:00:00 2001 From: Changaco Date: Wed, 16 Jul 2014 16:56:38 +0200 Subject: [PATCH 34/97] make participant IDs stable across test runs --- gittip/testing/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/gittip/testing/__init__.py b/gittip/testing/__init__.py index 8c2a0ae7fa..bf3a4d0872 100644 --- a/gittip/testing/__init__.py +++ b/gittip/testing/__init__.py @@ -104,6 +104,7 @@ def clear_tables(self): self.db.run("DELETE FROM %s CASCADE" % tablename) except (IntegrityError, InternalError): tablenames.insert(0, tablename) + self.db.run("ALTER SEQUENCE participants_id_seq RESTART WITH 1") def make_elsewhere(self, platform, user_id, user_name, **kw): From 74633e4653ba0705303520d96e6dcb997c435bba Mon Sep 17 00:00:00 2001 From: Changaco Date: Tue, 15 Jul 2014 18:11:21 +0200 Subject: [PATCH 35/97] update test fixtures --- tests/py/fixtures/TestCardHolds.yml | 1034 +++++++++++++++++++++++++++ tests/py/fixtures/TestCredits.yml | 71 ++ tests/py/fixtures/TestPayday.yml | 330 ++++++--- tests/py/fixtures/TestPayin.yml | 374 ++++++++++ 4 files changed, 1720 insertions(+), 89 deletions(-) create mode 100644 tests/py/fixtures/TestCardHolds.yml create mode 100644 tests/py/fixtures/TestCredits.yml create mode 100644 tests/py/fixtures/TestPayin.yml diff --git a/tests/py/fixtures/TestCardHolds.yml b/tests/py/fixtures/TestCardHolds.yml new file mode 100644 index 0000000000..6fcf2aedea --- /dev/null +++ b/tests/py/fixtures/TestCardHolds.yml @@ -0,0 +1,1034 @@ +interactions: +- request: + body: null + headers: {} + method: GET + uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd + response: + body: + string: !!binary | + H4sIAAAAAAAAA41Uy27bMBC85ysEnWvLipQ29i1oby2CAG3RokUh0CQLEZFIhY+ggqF/79KWZJpU + wlx02J0ZcmdHPFwlSYqN0qKlUqW75DcUkuRw/EKLo5ZClZumeTfVGsYfLXQCAUwJI7EPhDqhSjOO + NBN8VBlFhlnNdARpSiqkAZFeb/Jytfmw2uTf8ptdWezKcp0Xxfvb7a90pmBJX6UU622+uSlKh0LE + vmoF17U/DCP22I/fi88/Sf98//UT7b88FN3dD7mtn8j5zK4W3B8wrSX9a+nZ7GAWVwKjcY24rpRG + 2lgjUy5WLdK4Ph/XUo08i+mTheZp4KCdradI+qMhQiRV3qYw070HhD3BSun1cjkPy/bivhcg0glo + NBUWZKGJheFa9m43GGRvFNxDqWopdUrxqkFKl/6YtEWsCYrsInADnPXH5sdO6oT3HP31HOE0A9eO + eVbZYV7s2B/GFTlEjCSpatGQ4yqdKDhkRobMwZ2S7GjsEX+sED6aFJG5hAZKhO5ZTGLELHDdv/UF + H5xfesEM+k9TySEFbxsmhAeXsrZFLDlBAiZhqjOaRsgzKuBribhC2D5eEY0LZKADj4ThsRkm0AL7 + GV5m1ESuIOkECxSEJKe3/ZVwjpiAC08tieZpAsH/NVwN/wHQiK4AUwYAAA== + headers: + content-encoding: [gzip] + content-length: ['499'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: null + headers: {} + method: GET + uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd/cards?limit=10&offset=0 + response: + body: + string: !!binary | + H4sIAAAAAAAAA6VVW2+bMBR+76+weNhTmwBOyEWqprSbVq1NWq1dO3WakIPtxiqxmW2yoCr/fbaB + AL1o08YDEud+vvOdw9MBAF6CJFbeFHw3HwA8ubcRp4w/WnEtsJa50mJNpJF6p1/h+TdcbBbXH0hx + cQWz2Z2crH5ir/LfHdaBKOMPRGaScW0dx8mI+oMQwmiI6HicQAKjhPrhaBCRBEUDihMKI4yTkAQU + Tow5SSYUUor9IQqjkbcPnGw2sSQqT21cnqfpXsPz9bIsc9t6AvM03mij4kwojdJ4jXSysrVx0ejJ + NmMSaSZ4vBZcW33g7zOsiUZdcCR5MLYOGsFpruxHbW6xY7qItfjlLNoKhLFpQsWhVZyILQhfQsiw + i3sK52cn6i7anKELqPKLW3xzu7yctBBBmjwIWVhroVcGgn3BusiIFSeSYKa7INb9dzBcIv4Yc7R2 + Xp+v5pdfPs0W4PRsdv0RnMwW54dg0Zv1ungqLQnRDZ4FUY3BUiLu+rhlCjXiPMOmahwjx47QDwZH + /ujID26C4XQAp4NhbzwMQhjct1KVkBn7NjkNwEbS7qFkMbHIviIObD4TGcwR4+Dald4ejKGGds2/ + Os+KOonAzmbih4Hf9k5EzrUs4srA5n+xGQniMSZLM44p0DInDX9L2DvzaPGxIMjuYOiHDSHNMjxr + 02Mq3hDJKCMW9k4CS/796nhl76BiIjBjAmV7wFYPsABcaOBY0hq4rb5i0xRQlKqm/pUk1KLSd7el + /xfEleQPJBhEcBLA+3I3dgbKHxYtr7OHXoqUY1G/vlQm9xt3qiztfcrWTB8H/jtBqSL6uJ6hx8m2 + e1a8pqnqDP57cJfVFFofFK/MbiTVQL1Mkg0TuT3ADQs8Lcy5sn4lUzzK5P83bGB0x/rZxS//Cz3M + VJZrs8fNOJ/cVHsM7/p7bVVQqWn/J1qjqPxq7a5alyqRdY1XInX/opo5rVQtfSeZW6A3fCqd7fBg + 9xv4QHhu7QYAAA== + headers: + content-encoding: [gzip] + content-length: ['751'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: '{"amount": 2091, "meta": {"state": "new", "participant_id": 2}, "description": + "janet"}' + headers: {} + method: POST + uri: https://api.balancedpayments.com:443/cards/CC3MHBsW6vHaL3suLVdTVbO9/card_holds + response: + body: {string: !!python/unicode "{\n \"card_holds\": [\n {\n \"status\": + \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"card\": + \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \"debit\": null\n },\n \"updated_at\": + \"2014-07-28T12:44:02.482667Z\",\n \"created_at\": \"2014-07-28T12:44:02.246126Z\",\n + \ \"transaction_number\": \"HL223-221-7905\",\n \"expires_at\": \"2014-08-04T12:44:02.393923Z\",\n + \ \"failure_reason\": null,\n \"currency\": \"USD\",\n \"amount\": + 2091,\n \"meta\": {\n \"state\": \"new\",\n \"participant_id\": + \"2\"\n },\n \"href\": \"/card_holds/HL6DNvcbs8HPF3Hk2S7ktMY5\",\n + \ \"failure_reason_code\": null,\n \"voided_at\": null,\n \"id\": + \"HL6DNvcbs8HPF3Hk2S7ktMY5\"\n }\n ],\n \"links\": {\n \"card_holds.events\": + \"/card_holds/{card_holds.id}/events\",\n \"card_holds.card\": \"/cards/{card_holds.card}\",\n + \ \"card_holds.debits\": \"/card_holds/{card_holds.id}/debits\",\n \"card_holds.debit\": + \"/debits/{card_holds.debit}\"\n }\n}"} + headers: + content-length: ['944'] + content-type: [application/json] + status: {code: 201, message: CREATED} +- request: + body: '{"amount": 1000, "description": "janet"}' + headers: {} + method: POST + uri: https://api.balancedpayments.com:443/card_holds/HL6DNvcbs8HPF3Hk2S7ktMY5/debits + response: + body: {string: !!python/unicode "{\n \"debits\": [\n {\n \"status\": + \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"customer\": + \"CU3KXdyvNSDeyLP3pAWr9hqd\",\n \"source\": \"CC3MHBsW6vHaL3suLVdTVbO9\",\n + \ \"order\": null,\n \"dispute\": null\n },\n \"updated_at\": + \"2014-07-28T12:44:03.374049Z\",\n \"created_at\": \"2014-07-28T12:44:02.942161Z\",\n + \ \"transaction_number\": \"W481-816-9765\",\n \"failure_reason\": + null,\n \"currency\": \"USD\",\n \"amount\": 1000,\n \"failure_reason_code\": + null,\n \"meta\": {\n \"state\": \"new\",\n \"participant_id\": + \"2\"\n },\n \"href\": \"/debits/WD6EAiSkHLrwka8kNBgz40yd\",\n \"appears_on_statement_as\": + \"BAL*example.com\",\n \"id\": \"WD6EAiSkHLrwka8kNBgz40yd\"\n }\n + \ ],\n \"links\": {\n \"debits.customer\": \"/customers/{debits.customer}\",\n + \ \"debits.dispute\": \"/disputes/{debits.dispute}\",\n \"debits.source\": + \"/resources/{debits.source}\",\n \"debits.order\": \"/orders/{debits.order}\",\n + \ \"debits.refunds\": \"/debits/{debits.id}/refunds\",\n \"debits.events\": + \"/debits/{debits.id}/events\"\n }\n}"} + headers: + content-length: ['1063'] + content-type: [application/json] + status: {code: 201, message: CREATED} +- request: + body: '{"status": "succeeded", "description": "janet", "amount": 2091, "created_at": + "2014-07-28T12:44:02.246126Z", "card_href": "/cards/CC3MHBsW6vHaL3suLVdTVbO9", + "updated_at": "2014-07-28T12:44:02.482667Z", "expires_at": "2014-08-04T12:44:02.393923Z", + "failure_reason": null, "currency": "USD", "transaction_number": "HL223-221-7905", + "meta": {"state": "captured", "participant_id": "2"}, "debit": null, "failure_reason_code": + null, "voided_at": null, "id": "HL6DNvcbs8HPF3Hk2S7ktMY5"}' + headers: {} + method: PUT + uri: https://api.balancedpayments.com:443/card_holds/HL6DNvcbs8HPF3Hk2S7ktMY5 + response: + body: {string: !!python/unicode "{\n \"card_holds\": [\n {\n \"status\": + \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"card\": + \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \"debit\": \"WD6EAiSkHLrwka8kNBgz40yd\"\n + \ },\n \"updated_at\": \"2014-07-28T12:44:03.779589Z\",\n \"created_at\": + \"2014-07-28T12:44:02.246126Z\",\n \"transaction_number\": \"HL223-221-7905\",\n + \ \"expires_at\": \"2014-08-04T12:44:02.393923Z\",\n \"failure_reason\": + null,\n \"currency\": \"USD\",\n \"amount\": 2091,\n \"meta\": + {\n \"state\": \"captured\",\n \"participant_id\": \"2\"\n },\n + \ \"href\": \"/card_holds/HL6DNvcbs8HPF3Hk2S7ktMY5\",\n \"failure_reason_code\": + null,\n \"voided_at\": null,\n \"id\": \"HL6DNvcbs8HPF3Hk2S7ktMY5\"\n + \ }\n ],\n \"links\": {\n \"card_holds.events\": \"/card_holds/{card_holds.id}/events\",\n + \ \"card_holds.card\": \"/cards/{card_holds.card}\",\n \"card_holds.debits\": + \"/card_holds/{card_holds.id}/debits\",\n \"card_holds.debit\": \"/debits/{card_holds.debit}\"\n + \ }\n}"} + headers: + content-length: ['971'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: null + headers: {} + method: GET + uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd + response: + body: + string: !!binary | + H4sIAAAAAAAAA41Uy27bMBC85ysEnWvLipQ29i1oby2CAG3RokUh0CQLEZFIhY+ggqF/79KWZJpU + wlx02J0ZcmdHPFwlSYqN0qKlUqW75DcUkuRw/EKLo5ZClZumeTfVGsYfLXQCAUwJI7EPhDqhSjOO + NBN8VBlFhlnNdARpSiqkAZFeb/Jytfmw2uTf8ptdWezKcp0Xxfvb7a90pmBJX6UU622+uSlKh0LE + vmoF17U/DCP22I/fi88/Sf98//UT7b88FN3dD7mtn8j5zK4W3B8wrSX9a+nZ7GAWVwKjcY24rpRG + 2lgjUy5WLdK4Ph/XUo08i+mTheZp4KCdradI+qMhQiRV3qYw070HhD3BSun1cjkPy/bivhcg0glo + NBUWZKGJheFa9m43GGRvFNxDqWopdUrxqkFKl/6YtEWsCYrsInADnPXH5sdO6oT3HP31HOE0A9eO + eVbZYV7s2B/GFTlEjCSpatGQ4yqdKDhkRobMwZ2S7GjsEX+sED6aFJG5hAZKhO5ZTGLELHDdv/UF + H5xfesEM+k9TySEFbxsmhAeXsrZFLDlBAiZhqjOaRsgzKuBribhC2D5eEY0LZKADj4ThsRkm0AL7 + GV5m1ESuIOkECxSEJKe3/ZVwjpiAC08tieZpAsH/NVwN/wHQiK4AUwYAAA== + headers: + content-encoding: [gzip] + content-length: ['499'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: null + headers: {} + method: GET + uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd/cards?limit=10&offset=0 + response: + body: + string: !!binary | + H4sIAAAAAAAAA6VVW2+bMBR+76+weNhTmwBOyEWqprSbVq1NWq1dO3WakIPtxiqxmW2yoCr/fbaB + AL1o08YDEud+vvOdw9MBAF6CJFbeFHw3HwA8ubcRp4w/WnEtsJa50mJNpJF6p1/h+TdcbBbXH0hx + cQWz2Z2crH5ir/LfHdaBKOMPRGaScW0dx8mI+oMQwmiI6HicQAKjhPrhaBCRBEUDihMKI4yTkAQU + Tow5SSYUUor9IQqjkbcPnGw2sSQqT21cnqfpXsPz9bIsc9t6AvM03mij4kwojdJ4jXSysrVx0ejJ + NmMSaSZ4vBZcW33g7zOsiUZdcCR5MLYOGsFpruxHbW6xY7qItfjlLNoKhLFpQsWhVZyILQhfQsiw + i3sK52cn6i7anKELqPKLW3xzu7yctBBBmjwIWVhroVcGgn3BusiIFSeSYKa7INb9dzBcIv4Yc7R2 + Xp+v5pdfPs0W4PRsdv0RnMwW54dg0Zv1ungqLQnRDZ4FUY3BUiLu+rhlCjXiPMOmahwjx47QDwZH + /ujID26C4XQAp4NhbzwMQhjct1KVkBn7NjkNwEbS7qFkMbHIviIObD4TGcwR4+Dald4ejKGGds2/ + Os+KOonAzmbih4Hf9k5EzrUs4srA5n+xGQniMSZLM44p0DInDX9L2DvzaPGxIMjuYOiHDSHNMjxr + 02Mq3hDJKCMW9k4CS/796nhl76BiIjBjAmV7wFYPsABcaOBY0hq4rb5i0xRQlKqm/pUk1KLSd7el + /xfEleQPJBhEcBLA+3I3dgbKHxYtr7OHXoqUY1G/vlQm9xt3qiztfcrWTB8H/jtBqSL6uJ6hx8m2 + e1a8pqnqDP57cJfVFFofFK/MbiTVQL1Mkg0TuT3ADQs8Lcy5sn4lUzzK5P83bGB0x/rZxS//Cz3M + VJZrs8fNOJ/cVHsM7/p7bVVQqWn/J1qjqPxq7a5alyqRdY1XInX/opo5rVQtfSeZW6A3fCqd7fBg + 9xv4QHhu7QYAAA== + headers: + content-encoding: [gzip] + content-length: ['751'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: '{"amount": 2091, "meta": {"state": "new", "participant_id": 2}, "description": + "janet"}' + headers: {} + method: POST + uri: https://api.balancedpayments.com:443/cards/CC3MHBsW6vHaL3suLVdTVbO9/card_holds + response: + body: {string: !!python/unicode "{\n \"card_holds\": [\n {\n \"status\": + \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"card\": + \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \"debit\": null\n },\n \"updated_at\": + \"2014-07-28T12:44:05.151262Z\",\n \"created_at\": \"2014-07-28T12:44:04.938351Z\",\n + \ \"transaction_number\": \"HL278-301-3242\",\n \"expires_at\": \"2014-08-04T12:44:05.064676Z\",\n + \ \"failure_reason\": null,\n \"currency\": \"USD\",\n \"amount\": + 2091,\n \"meta\": {\n \"state\": \"new\",\n \"participant_id\": + \"2\"\n },\n \"href\": \"/card_holds/HL6GP82LFspeIjW9XWztLEwE\",\n + \ \"failure_reason_code\": null,\n \"voided_at\": null,\n \"id\": + \"HL6GP82LFspeIjW9XWztLEwE\"\n }\n ],\n \"links\": {\n \"card_holds.events\": + \"/card_holds/{card_holds.id}/events\",\n \"card_holds.card\": \"/cards/{card_holds.card}\",\n + \ \"card_holds.debits\": \"/card_holds/{card_holds.id}/debits\",\n \"card_holds.debit\": + \"/debits/{card_holds.debit}\"\n }\n}"} + headers: + content-length: ['944'] + content-type: [application/json] + status: {code: 201, message: CREATED} +- request: + body: '{"amount": 2091, "description": "janet"}' + headers: {} + method: POST + uri: https://api.balancedpayments.com:443/card_holds/HL6GP82LFspeIjW9XWztLEwE/debits + response: + body: {string: !!python/unicode "{\n \"debits\": [\n {\n \"status\": + \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"customer\": + \"CU3KXdyvNSDeyLP3pAWr9hqd\",\n \"source\": \"CC3MHBsW6vHaL3suLVdTVbO9\",\n + \ \"order\": null,\n \"dispute\": null\n },\n \"updated_at\": + \"2014-07-28T12:44:06.139206Z\",\n \"created_at\": \"2014-07-28T12:44:05.676736Z\",\n + \ \"transaction_number\": \"W013-682-4382\",\n \"failure_reason\": + null,\n \"currency\": \"USD\",\n \"amount\": 2091,\n \"failure_reason_code\": + null,\n \"meta\": {\n \"state\": \"new\",\n \"participant_id\": + \"2\"\n },\n \"href\": \"/debits/WD6HEBOYVE3p11E9idopswsd\",\n \"appears_on_statement_as\": + \"BAL*example.com\",\n \"id\": \"WD6HEBOYVE3p11E9idopswsd\"\n }\n + \ ],\n \"links\": {\n \"debits.customer\": \"/customers/{debits.customer}\",\n + \ \"debits.dispute\": \"/disputes/{debits.dispute}\",\n \"debits.source\": + \"/resources/{debits.source}\",\n \"debits.order\": \"/orders/{debits.order}\",\n + \ \"debits.refunds\": \"/debits/{debits.id}/refunds\",\n \"debits.events\": + \"/debits/{debits.id}/events\"\n }\n}"} + headers: + content-length: ['1063'] + content-type: [application/json] + status: {code: 201, message: CREATED} +- request: + body: '{"status": "succeeded", "description": "janet", "amount": 2091, "created_at": + "2014-07-28T12:44:04.938351Z", "card_href": "/cards/CC3MHBsW6vHaL3suLVdTVbO9", + "updated_at": "2014-07-28T12:44:05.151262Z", "expires_at": "2014-08-04T12:44:05.064676Z", + "failure_reason": null, "currency": "USD", "transaction_number": "HL278-301-3242", + "meta": {"state": "captured", "participant_id": "2"}, "debit": null, "failure_reason_code": + null, "voided_at": null, "id": "HL6GP82LFspeIjW9XWztLEwE"}' + headers: {} + method: PUT + uri: https://api.balancedpayments.com:443/card_holds/HL6GP82LFspeIjW9XWztLEwE + response: + body: {string: !!python/unicode "{\n \"card_holds\": [\n {\n \"status\": + \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"card\": + \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \"debit\": \"WD6HEBOYVE3p11E9idopswsd\"\n + \ },\n \"updated_at\": \"2014-07-28T12:44:06.588345Z\",\n \"created_at\": + \"2014-07-28T12:44:04.938351Z\",\n \"transaction_number\": \"HL278-301-3242\",\n + \ \"expires_at\": \"2014-08-04T12:44:05.064676Z\",\n \"failure_reason\": + null,\n \"currency\": \"USD\",\n \"amount\": 2091,\n \"meta\": + {\n \"state\": \"captured\",\n \"participant_id\": \"2\"\n },\n + \ \"href\": \"/card_holds/HL6GP82LFspeIjW9XWztLEwE\",\n \"failure_reason_code\": + null,\n \"voided_at\": null,\n \"id\": \"HL6GP82LFspeIjW9XWztLEwE\"\n + \ }\n ],\n \"links\": {\n \"card_holds.events\": \"/card_holds/{card_holds.id}/events\",\n + \ \"card_holds.card\": \"/cards/{card_holds.card}\",\n \"card_holds.debits\": + \"/card_holds/{card_holds.id}/debits\",\n \"card_holds.debit\": \"/debits/{card_holds.debit}\"\n + \ }\n}"} + headers: + content-length: ['971'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: null + headers: {} + method: GET + uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd + response: + body: + string: !!binary | + H4sIAAAAAAAAA41Uy27bMBC85ysEnWvLipQ29i1oby2CAG3RokUh0CQLEZFIhY+ggqF/79KWZJpU + wlx02J0ZcmdHPFwlSYqN0qKlUqW75DcUkuRw/EKLo5ZClZumeTfVGsYfLXQCAUwJI7EPhDqhSjOO + NBN8VBlFhlnNdARpSiqkAZFeb/Jytfmw2uTf8ptdWezKcp0Xxfvb7a90pmBJX6UU622+uSlKh0LE + vmoF17U/DCP22I/fi88/Sf98//UT7b88FN3dD7mtn8j5zK4W3B8wrSX9a+nZ7GAWVwKjcY24rpRG + 2lgjUy5WLdK4Ph/XUo08i+mTheZp4KCdradI+qMhQiRV3qYw070HhD3BSun1cjkPy/bivhcg0glo + NBUWZKGJheFa9m43GGRvFNxDqWopdUrxqkFKl/6YtEWsCYrsInADnPXH5sdO6oT3HP31HOE0A9eO + eVbZYV7s2B/GFTlEjCSpatGQ4yqdKDhkRobMwZ2S7GjsEX+sED6aFJG5hAZKhO5ZTGLELHDdv/UF + H5xfesEM+k9TySEFbxsmhAeXsrZFLDlBAiZhqjOaRsgzKuBribhC2D5eEY0LZKADj4ThsRkm0AL7 + GV5m1ESuIOkECxSEJKe3/ZVwjpiAC08tieZpAsH/NVwN/wHQiK4AUwYAAA== + headers: + content-encoding: [gzip] + content-length: ['499'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: null + headers: {} + method: GET + uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd/cards?limit=10&offset=0 + response: + body: + string: !!binary | + H4sIAAAAAAAAA6VVW2+bMBR+76+weNhTmwBOyEWqprSbVq1NWq1dO3WakIPtxiqxmW2yoCr/fbaB + AL1o08YDEud+vvOdw9MBAF6CJFbeFHw3HwA8ubcRp4w/WnEtsJa50mJNpJF6p1/h+TdcbBbXH0hx + cQWz2Z2crH5ir/LfHdaBKOMPRGaScW0dx8mI+oMQwmiI6HicQAKjhPrhaBCRBEUDihMKI4yTkAQU + Tow5SSYUUor9IQqjkbcPnGw2sSQqT21cnqfpXsPz9bIsc9t6AvM03mij4kwojdJ4jXSysrVx0ejJ + NmMSaSZ4vBZcW33g7zOsiUZdcCR5MLYOGsFpruxHbW6xY7qItfjlLNoKhLFpQsWhVZyILQhfQsiw + i3sK52cn6i7anKELqPKLW3xzu7yctBBBmjwIWVhroVcGgn3BusiIFSeSYKa7INb9dzBcIv4Yc7R2 + Xp+v5pdfPs0W4PRsdv0RnMwW54dg0Zv1ungqLQnRDZ4FUY3BUiLu+rhlCjXiPMOmahwjx47QDwZH + /ujID26C4XQAp4NhbzwMQhjct1KVkBn7NjkNwEbS7qFkMbHIviIObD4TGcwR4+Dald4ejKGGds2/ + Os+KOonAzmbih4Hf9k5EzrUs4srA5n+xGQniMSZLM44p0DInDX9L2DvzaPGxIMjuYOiHDSHNMjxr + 02Mq3hDJKCMW9k4CS/796nhl76BiIjBjAmV7wFYPsABcaOBY0hq4rb5i0xRQlKqm/pUk1KLSd7el + /xfEleQPJBhEcBLA+3I3dgbKHxYtr7OHXoqUY1G/vlQm9xt3qiztfcrWTB8H/jtBqSL6uJ6hx8m2 + e1a8pqnqDP57cJfVFFofFK/MbiTVQL1Mkg0TuT3ADQs8Lcy5sn4lUzzK5P83bGB0x/rZxS//Cz3M + VJZrs8fNOJ/cVHsM7/p7bVVQqWn/J1qjqPxq7a5alyqRdY1XInX/opo5rVQtfSeZW6A3fCqd7fBg + 9xv4QHhu7QYAAA== + headers: + content-encoding: [gzip] + content-length: ['751'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: '{"amount": 2091, "meta": {"state": "new", "participant_id": 2}, "description": + "janet"}' + headers: {} + method: POST + uri: https://api.balancedpayments.com:443/cards/CC3MHBsW6vHaL3suLVdTVbO9/card_holds + response: + body: {string: !!python/unicode "{\n \"card_holds\": [\n {\n \"status\": + \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"card\": + \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \"debit\": null\n },\n \"updated_at\": + \"2014-07-28T12:44:08.073612Z\",\n \"created_at\": \"2014-07-28T12:44:07.849448Z\",\n + \ \"transaction_number\": \"HL787-086-5782\",\n \"expires_at\": \"2014-08-04T12:44:07.976594Z\",\n + \ \"failure_reason\": null,\n \"currency\": \"USD\",\n \"amount\": + 2091,\n \"meta\": {\n \"state\": \"new\",\n \"participant_id\": + \"2\"\n },\n \"href\": \"/card_holds/HL6K6eJyyZDc7Wa2YNTWgl1P\",\n + \ \"failure_reason_code\": null,\n \"voided_at\": null,\n \"id\": + \"HL6K6eJyyZDc7Wa2YNTWgl1P\"\n }\n ],\n \"links\": {\n \"card_holds.events\": + \"/card_holds/{card_holds.id}/events\",\n \"card_holds.card\": \"/cards/{card_holds.card}\",\n + \ \"card_holds.debits\": \"/card_holds/{card_holds.id}/debits\",\n \"card_holds.debit\": + \"/debits/{card_holds.debit}\"\n }\n}"} + headers: + content-length: ['944'] + content-type: [application/json] + status: {code: 201, message: CREATED} +- request: + body: '{"amount": 1576, "description": "janet"}' + headers: {} + method: POST + uri: https://api.balancedpayments.com:443/card_holds/HL6K6eJyyZDc7Wa2YNTWgl1P/debits + response: + body: {string: !!python/unicode "{\n \"debits\": [\n {\n \"status\": + \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"customer\": + \"CU3KXdyvNSDeyLP3pAWr9hqd\",\n \"source\": \"CC3MHBsW6vHaL3suLVdTVbO9\",\n + \ \"order\": null,\n \"dispute\": null\n },\n \"updated_at\": + \"2014-07-28T12:44:08.888162Z\",\n \"created_at\": \"2014-07-28T12:44:08.477253Z\",\n + \ \"transaction_number\": \"W346-884-7108\",\n \"failure_reason\": + null,\n \"currency\": \"USD\",\n \"amount\": 1576,\n \"failure_reason_code\": + null,\n \"meta\": {\n \"state\": \"new\",\n \"participant_id\": + \"2\"\n },\n \"href\": \"/debits/WD6KNTVQcbvgalfEe3OD7CRv\",\n \"appears_on_statement_as\": + \"BAL*example.com\",\n \"id\": \"WD6KNTVQcbvgalfEe3OD7CRv\"\n }\n + \ ],\n \"links\": {\n \"debits.customer\": \"/customers/{debits.customer}\",\n + \ \"debits.dispute\": \"/disputes/{debits.dispute}\",\n \"debits.source\": + \"/resources/{debits.source}\",\n \"debits.order\": \"/orders/{debits.order}\",\n + \ \"debits.refunds\": \"/debits/{debits.id}/refunds\",\n \"debits.events\": + \"/debits/{debits.id}/events\"\n }\n}"} + headers: + content-length: ['1063'] + content-type: [application/json] + status: {code: 201, message: CREATED} +- request: + body: '{"status": "succeeded", "description": "janet", "amount": 2091, "created_at": + "2014-07-28T12:44:07.849448Z", "card_href": "/cards/CC3MHBsW6vHaL3suLVdTVbO9", + "updated_at": "2014-07-28T12:44:08.073612Z", "expires_at": "2014-08-04T12:44:07.976594Z", + "failure_reason": null, "currency": "USD", "transaction_number": "HL787-086-5782", + "meta": {"state": "captured", "participant_id": "2"}, "debit": null, "failure_reason_code": + null, "voided_at": null, "id": "HL6K6eJyyZDc7Wa2YNTWgl1P"}' + headers: {} + method: PUT + uri: https://api.balancedpayments.com:443/card_holds/HL6K6eJyyZDc7Wa2YNTWgl1P + response: + body: {string: !!python/unicode "{\n \"card_holds\": [\n {\n \"status\": + \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"card\": + \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \"debit\": \"WD6KNTVQcbvgalfEe3OD7CRv\"\n + \ },\n \"updated_at\": \"2014-07-28T12:44:09.290268Z\",\n \"created_at\": + \"2014-07-28T12:44:07.849448Z\",\n \"transaction_number\": \"HL787-086-5782\",\n + \ \"expires_at\": \"2014-08-04T12:44:07.976594Z\",\n \"failure_reason\": + null,\n \"currency\": \"USD\",\n \"amount\": 2091,\n \"meta\": + {\n \"state\": \"captured\",\n \"participant_id\": \"2\"\n },\n + \ \"href\": \"/card_holds/HL6K6eJyyZDc7Wa2YNTWgl1P\",\n \"failure_reason_code\": + null,\n \"voided_at\": null,\n \"id\": \"HL6K6eJyyZDc7Wa2YNTWgl1P\"\n + \ }\n ],\n \"links\": {\n \"card_holds.events\": \"/card_holds/{card_holds.id}/events\",\n + \ \"card_holds.card\": \"/cards/{card_holds.card}\",\n \"card_holds.debits\": + \"/card_holds/{card_holds.id}/debits\",\n \"card_holds.debit\": \"/debits/{card_holds.debit}\"\n + \ }\n}"} + headers: + content-length: ['971'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: null + headers: {} + method: GET + uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd + response: + body: + string: !!binary | + H4sIAAAAAAAAA41Uy27bMBC85ysEnWvLipQ29i1oby2CAG3RokUh0CQLEZFIhY+ggqF/79KWZJpU + wlx02J0ZcmdHPFwlSYqN0qKlUqW75DcUkuRw/EKLo5ZClZumeTfVGsYfLXQCAUwJI7EPhDqhSjOO + NBN8VBlFhlnNdARpSiqkAZFeb/Jytfmw2uTf8ptdWezKcp0Xxfvb7a90pmBJX6UU622+uSlKh0LE + vmoF17U/DCP22I/fi88/Sf98//UT7b88FN3dD7mtn8j5zK4W3B8wrSX9a+nZ7GAWVwKjcY24rpRG + 2lgjUy5WLdK4Ph/XUo08i+mTheZp4KCdradI+qMhQiRV3qYw070HhD3BSun1cjkPy/bivhcg0glo + NBUWZKGJheFa9m43GGRvFNxDqWopdUrxqkFKl/6YtEWsCYrsInADnPXH5sdO6oT3HP31HOE0A9eO + eVbZYV7s2B/GFTlEjCSpatGQ4yqdKDhkRobMwZ2S7GjsEX+sED6aFJG5hAZKhO5ZTGLELHDdv/UF + H5xfesEM+k9TySEFbxsmhAeXsrZFLDlBAiZhqjOaRsgzKuBribhC2D5eEY0LZKADj4ThsRkm0AL7 + GV5m1ESuIOkECxSEJKe3/ZVwjpiAC08tieZpAsH/NVwN/wHQiK4AUwYAAA== + headers: + content-encoding: [gzip] + content-length: ['499'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: null + headers: {} + method: GET + uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd/cards?limit=10&offset=0 + response: + body: + string: !!binary | + H4sIAAAAAAAAA6VVW2+bMBR+76+weNhTmwBOyEWqprSbVq1NWq1dO3WakIPtxiqxmW2yoCr/fbaB + AL1o08YDEud+vvOdw9MBAF6CJFbeFHw3HwA8ubcRp4w/WnEtsJa50mJNpJF6p1/h+TdcbBbXH0hx + cQWz2Z2crH5ir/LfHdaBKOMPRGaScW0dx8mI+oMQwmiI6HicQAKjhPrhaBCRBEUDihMKI4yTkAQU + Tow5SSYUUor9IQqjkbcPnGw2sSQqT21cnqfpXsPz9bIsc9t6AvM03mij4kwojdJ4jXSysrVx0ejJ + NmMSaSZ4vBZcW33g7zOsiUZdcCR5MLYOGsFpruxHbW6xY7qItfjlLNoKhLFpQsWhVZyILQhfQsiw + i3sK52cn6i7anKELqPKLW3xzu7yctBBBmjwIWVhroVcGgn3BusiIFSeSYKa7INb9dzBcIv4Yc7R2 + Xp+v5pdfPs0W4PRsdv0RnMwW54dg0Zv1ungqLQnRDZ4FUY3BUiLu+rhlCjXiPMOmahwjx47QDwZH + /ujID26C4XQAp4NhbzwMQhjct1KVkBn7NjkNwEbS7qFkMbHIviIObD4TGcwR4+Dald4ejKGGds2/ + Os+KOonAzmbih4Hf9k5EzrUs4srA5n+xGQniMSZLM44p0DInDX9L2DvzaPGxIMjuYOiHDSHNMjxr + 02Mq3hDJKCMW9k4CS/796nhl76BiIjBjAmV7wFYPsABcaOBY0hq4rb5i0xRQlKqm/pUk1KLSd7el + /xfEleQPJBhEcBLA+3I3dgbKHxYtr7OHXoqUY1G/vlQm9xt3qiztfcrWTB8H/jtBqSL6uJ6hx8m2 + e1a8pqnqDP57cJfVFFofFK/MbiTVQL1Mkg0TuT3ADQs8Lcy5sn4lUzzK5P83bGB0x/rZxS//Cz3M + VJZrs8fNOJ/cVHsM7/p7bVVQqWn/J1qjqPxq7a5alyqRdY1XInX/opo5rVQtfSeZW6A3fCqd7fBg + 9xv4QHhu7QYAAA== + headers: + content-encoding: [gzip] + content-length: ['751'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: '{"amount": 2091, "meta": {"state": "new", "participant_id": 2}, "description": + "janet"}' + headers: {} + method: POST + uri: https://api.balancedpayments.com:443/cards/CC3MHBsW6vHaL3suLVdTVbO9/card_holds + response: + body: {string: !!python/unicode "{\n \"card_holds\": [\n {\n \"status\": + \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"card\": + \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \"debit\": null\n },\n \"updated_at\": + \"2014-07-28T12:44:10.526231Z\",\n \"created_at\": \"2014-07-28T12:44:10.311057Z\",\n + \ \"transaction_number\": \"HL886-348-7865\",\n \"expires_at\": \"2014-08-04T12:44:10.436799Z\",\n + \ \"failure_reason\": null,\n \"currency\": \"USD\",\n \"amount\": + 2091,\n \"meta\": {\n \"state\": \"new\",\n \"participant_id\": + \"2\"\n },\n \"href\": \"/card_holds/HL6MShgF10sXHw6r21W3OTLX\",\n + \ \"failure_reason_code\": null,\n \"voided_at\": null,\n \"id\": + \"HL6MShgF10sXHw6r21W3OTLX\"\n }\n ],\n \"links\": {\n \"card_holds.events\": + \"/card_holds/{card_holds.id}/events\",\n \"card_holds.card\": \"/cards/{card_holds.card}\",\n + \ \"card_holds.debits\": \"/card_holds/{card_holds.id}/debits\",\n \"card_holds.debit\": + \"/debits/{card_holds.debit}\"\n }\n}"} + headers: + content-length: ['944'] + content-type: [application/json] + status: {code: 201, message: CREATED} +- request: + body: '{"amount": 2092, "description": "janet"}' + headers: {} + method: POST + uri: https://api.balancedpayments.com:443/card_holds/HL6MShgF10sXHw6r21W3OTLX/debits + response: + body: {string: !!python/unicode "{\n \"errors\": [\n {\n \"status\": + \"Bad Request\",\n \"category_code\": \"request\",\n \"additional\": + null,\n \"status_code\": 400,\n \"category_type\": \"request\",\n + \ \"extras\": {\n \"amount\": \"\\\"2092\\\" must be <= 2091\"\n + \ },\n \"request_id\": \"OHMdf6d676e165411e4b88e02b12035401b\",\n + \ \"description\": \"Invalid field [amount] - \\\"2092\\\" must be <= + 2091 Your request id is OHMdf6d676e165411e4b88e02b12035401b.\"\n }\n ]\n}"} + headers: + content-length: ['444'] + content-type: [application/json] + status: {code: 400, message: BAD REQUEST} +- request: + body: '{"status": "succeeded", "transaction_number": "HL886-348-7865", "description": + "janet", "amount": 2091, "created_at": "2014-07-28T12:44:10.311057Z", "card_href": + "/cards/CC3MHBsW6vHaL3suLVdTVbO9", "updated_at": "2014-07-28T12:44:10.526231Z", + "expires_at": "2014-08-04T12:44:10.436799Z", "failure_reason": null, "currency": + "USD", "is_void": true, "meta": {"state": "cancelled", "participant_id": "2"}, + "debit": null, "failure_reason_code": null, "voided_at": null, "id": "HL6MShgF10sXHw6r21W3OTLX"}' + headers: {} + method: PUT + uri: https://api.balancedpayments.com:443/card_holds/HL6MShgF10sXHw6r21W3OTLX + response: + body: {string: !!python/unicode "{\n \"card_holds\": [\n {\n \"status\": + \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"card\": + \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \"debit\": null\n },\n \"updated_at\": + \"2014-07-28T12:44:11.365477Z\",\n \"created_at\": \"2014-07-28T12:44:10.311057Z\",\n + \ \"transaction_number\": \"HL886-348-7865\",\n \"expires_at\": \"2014-08-04T12:44:10.436799Z\",\n + \ \"failure_reason\": null,\n \"currency\": \"USD\",\n \"amount\": + 2091,\n \"meta\": {\n \"state\": \"cancelled\",\n \"participant_id\": + \"2\"\n },\n \"href\": \"/card_holds/HL6MShgF10sXHw6r21W3OTLX\",\n + \ \"failure_reason_code\": null,\n \"voided_at\": \"2014-07-28T12:44:11.365480Z\",\n + \ \"id\": \"HL6MShgF10sXHw6r21W3OTLX\"\n }\n ],\n \"links\": {\n + \ \"card_holds.events\": \"/card_holds/{card_holds.id}/events\",\n \"card_holds.card\": + \"/cards/{card_holds.card}\",\n \"card_holds.debits\": \"/card_holds/{card_holds.id}/debits\",\n + \ \"card_holds.debit\": \"/debits/{card_holds.debit}\"\n }\n}"} + headers: + content-length: ['975'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: '{"meta": {"seq": 3}}' + headers: {} + method: POST + uri: https://api.balancedpayments.com:443/customers + response: + body: {string: !!python/unicode "{\n \"customers\": [\n {\n \"name\": + null,\n \"links\": {\n \"source\": null,\n \"destination\": + null\n },\n \"updated_at\": \"2014-07-28T12:44:12.158196Z\",\n \"created_at\": + \"2014-07-28T12:44:12.031864Z\",\n \"dob_month\": null,\n \"id\": + \"CU6OOh9HBCcXISrxLboPWJbr\",\n \"phone\": null,\n \"href\": \"/customers/CU6OOh9HBCcXISrxLboPWJbr\",\n + \ \"merchant_status\": \"no-match\",\n \"meta\": {\n \"seq\": + \"3\"\n },\n \"dob_year\": null,\n \"address\": {\n \"city\": + null,\n \"line2\": null,\n \"line1\": null,\n \"state\": + null,\n \"postal_code\": null,\n \"country_code\": null\n },\n + \ \"business_name\": null,\n \"ssn_last4\": null,\n \"email\": + null,\n \"ein\": null\n }\n ],\n \"links\": {\n \"customers.source\": + \"/resources/{customers.source}\",\n \"customers.card_holds\": \"/customers/{customers.id}/card_holds\",\n + \ \"customers.bank_accounts\": \"/customers/{customers.id}/bank_accounts\",\n + \ \"customers.debits\": \"/customers/{customers.id}/debits\",\n \"customers.destination\": + \"/resources/{customers.destination}\",\n \"customers.external_accounts\": + \"/customers/{customers.id}/external_accounts\",\n \"customers.cards\": + \"/customers/{customers.id}/cards\",\n \"customers.disputes\": \"/customers/{customers.id}/disputes\",\n + \ \"customers.transactions\": \"/customers/{customers.id}/transactions\",\n + \ \"customers.refunds\": \"/customers/{customers.id}/refunds\",\n \"customers.reversals\": + \"/customers/{customers.id}/reversals\",\n \"customers.orders\": \"/customers/{customers.id}/orders\",\n + \ \"customers.credits\": \"/customers/{customers.id}/credits\"\n }\n}"} + headers: + content-length: ['1619'] + content-type: [application/json] + status: {code: 201, message: CREATED} +- request: + body: '{"expiration_month": 12, "number": "4444444444444448", "expiration_year": + 2020}' + headers: {} + method: POST + uri: https://api.balancedpayments.com:443/cards + response: + body: {string: !!python/unicode "{\n \"cards\": [\n {\n \"links\": {\n + \ \"customer\": null\n },\n \"fingerprint\": \"9ea2b317b53183f5a93ba23a594a0b8a0f2183ea9cc338e0964755cd9df71b99\",\n + \ \"cvv_result\": null,\n \"number\": \"xxxxxxxxxxxx4448\",\n \"avs_postal_match\": + null,\n \"expiration_month\": 12,\n \"meta\": {},\n \"id\": + \"CC6PZQCKGZAQsuwLSTBbRqId\",\n \"category\": \"other\",\n \"type\": + \"credit\",\n \"cvv_match\": null,\n \"bank_name\": \"CREDIT AGRICOLE + BANK POLSKA, S.A.\",\n \"avs_street_match\": null,\n \"brand\": + \"Visa\",\n \"updated_at\": \"2014-07-28T12:44:13.094156Z\",\n \"address\": + {\n \"city\": null,\n \"line2\": null,\n \"line1\": null,\n + \ \"state\": null,\n \"postal_code\": null,\n \"country_code\": + null\n },\n \"can_debit\": true,\n \"name\": null,\n \"expiration_year\": + 2020,\n \"cvv\": null,\n \"is_verified\": true,\n \"avs_result\": + null,\n \"can_credit\": false,\n \"href\": \"/cards/CC6PZQCKGZAQsuwLSTBbRqId\",\n + \ \"created_at\": \"2014-07-28T12:44:13.094153Z\"\n }\n ],\n \"links\": + {\n \"cards.card_holds\": \"/cards/{cards.id}/card_holds\",\n \"cards.customer\": + \"/customers/{cards.customer}\",\n \"cards.disputes\": \"/cards/{cards.id}/disputes\",\n + \ \"cards.debits\": \"/cards/{cards.id}/debits\"\n }\n}"} + headers: + access-control-allow-headers: [Content-Type] + access-control-allow-methods: ['POST, OPTIONS'] + access-control-allow-origin: ['*'] + content-length: ['1269'] + content-type: [application/json] + status: {code: 201, message: CREATED} +- request: + body: '{"links": {"customer": "/customers/CU6OOh9HBCcXISrxLboPWJbr"}, "cvv_result": + null, "number": "xxxxxxxxxxxx4448", "avs_postal_match": null, "expiration_month": + 12, "meta": {}, "id": "CC6PZQCKGZAQsuwLSTBbRqId", "category": "other", "type": + "credit", "cvv_match": null, "bank_name": "CREDIT AGRICOLE BANK POLSKA, S.A.", + "avs_street_match": null, "brand": "Visa", "updated_at": "2014-07-28T12:44:13.094156Z", + "fingerprint": "9ea2b317b53183f5a93ba23a594a0b8a0f2183ea9cc338e0964755cd9df71b99", + "can_debit": true, "customer": null, "name": null, "expiration_year": 2020, + "cvv": null, "is_verified": true, "avs_result": null, "can_credit": false, "created_at": + "2014-07-28T12:44:13.094153Z", "address": {"city": null, "line2": null, "line1": + null, "state": null, "postal_code": null, "country_code": null}}' + headers: {} + method: PUT + uri: https://api.balancedpayments.com:443/cards/CC6PZQCKGZAQsuwLSTBbRqId + response: + body: + string: !!binary | + H4sIAAAAAAAAA41U247aMBB936+I8rwLuULCG6SrLQUVFuhFVFVkx85ibeJQ22FBKP9eOxcILKs2 + D5FyJjNzzsyxj3eapkeAIa4PtF/yQ9OO5VvCCaGvCm4A9WfORZZiJlE9+NabzTb+51EQ/Rwv2X4K + s/mPL5DpdX5x3xSKCX3BbMsIFSrRx8CCttmHrm16duwC34bAsoHrO8CAHjBiS+IY+FFk2x42/J7T + d90I+Sjum9D39VPhaLcLGeZ5ourSPElOEZqnsKK5bz2O43jnbLDj4TbjAiRhCkS0ua6B91vCgCAZ + DdOMChU3rVOHFAughnOWSVA5lqA3Xz8Hk6f18Jnnb9PlagQXf8aoxRoI/JKxg/o7ExtJ81RUHLZY + wRHDiIhLoTc5QkBfQwrSMitYPH4ar7Th02IczKaP2mj4daLNZ9PlZHivLTvDzqV2LhjG4rZ2yAAt + 5XwnHJzT8i2S5FEIykVahuk8GP0Hy1uZ1sBxBqbd6Vk9w3PXrU4IyRVd+4gIJb+9sspw2LoNm+9h + uTihZF8VqTcaZehGMMpyKtghbEXfuTUCNEQYyvEPNMFyfPZUNeYLn7U8csBAnQvLsIy2Qa8Y6oSH + O8xITLCa70UDZcjbdlacak8MtBgk/Mxqw3AsC+nd8hR3/8N+DP9jh4bvmK69rg5yIQf0Wwm6ug+q + W6OjuoabLCkvkIbEseTSIagoWdXxaipNXusm6TbXCu/WmQ1Q1D6qkxDh21zgD1qdoheNylV+lFHF + pLjirvgLKWEQ5QsFAAA= + headers: + content-encoding: [gzip] + content-length: ['584'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: null + headers: {} + method: GET + uri: https://api.balancedpayments.com:443/customers/CU6OOh9HBCcXISrxLboPWJbr + response: + body: + string: !!binary | + H4sIAAAAAAAAA41UwY7TMBC971dEOdNNkw2l2yN7AYS0SIBAIBRNbKNYm9jFdtBWVf6dcZukrh0w + lxxm3nv2vHnx8SZJUtJrIzumdLpLvmMhSY6nL7YEdAyrom/bF1Ot5eLJQicQwrTsFfGBWKdMGy7A + cClGlVFkmNX6PQXDaAUGEWmxzsvV+tWq2H7Ki11Z7vLiNn+5ze8339KZQhSLUNZ3+XZTOhQq66qT + wjT+MJzaYx8+bx4fm/s3rx/I17cf1fP7Wn748q5WlzP3jRT+gGmj2E9Lz2YHs7gSGk0aEKbSBkxv + jUyFXHVgSHM5rmMGPIvZLwu9SwMH7WwHBsofDShVTHubItwcPCDuCVfKiuVyHpbtxX0vUGQvsdFW + RNKFJpG9MOrgdoNB6l7jPbSullKntaha0Kb0x2Qd8DYo8qvADXjWD5sfO6kT3kv0b+cIpxm6dsqz + zo7zYsf+MK7IIRJQtGpkS0+rdKLgkDkdMgd3TrKjUYN4qoCcTIrIXEMDJcpqHpMYMQtc92/9iw/O + L71gBns2TAlMwf8NE8KDS1nbIpacIQGTcr3vDYuQZ1TANwqEBmIfr4jGFTLQwUeiF7EZJtAC+ze+ + zNBGrqDYBAsUpKLnt/0f4RwxARefWhrN0wTC/2u4Gf4A2ILrwFMGAAA= + headers: + content-encoding: [gzip] + content-length: ['497'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: null + headers: {} + method: GET + uri: https://api.balancedpayments.com:443/customers/CU6OOh9HBCcXISrxLboPWJbr/cards?limit=10&offset=0 + response: + body: + string: !!binary | + H4sIAAAAAAAAA6VUbW/aMBD+3l8R+cM+tZBXIEjVRFnVsaJCS/cipilyYqdYTZzMdhio4r/PdhIS + KNWmjQ+RuPPdPXfPc/dyZhggggxxMDS+yz+G8aK/0pwQ+qzMtUG9LLjIUsykFYw/92azlf/xahx9 + myzYZhpm86+fQgaq+N15nSgm9AmznBEqVKCPoR06Vj/0HGvgxB70nRDaDvR8F5rhAJqxLe0Y+lHk + OANs+j2373kR8lHct0LfB/vE0XodMMyLROWlRZLsPbRIwxLmpvVzXXfQRMM1D/KMC5gEKRTR6jgH + 3uSEQUEyGqQZFcpv2fsKKRZQDadpkyA9lnFvvrwf394sR/e8+DVdPF6FDz8nqIUaCvyUsa16nYmV + hLlPKrY5VuaIYUTEYaMnMYaQPgcUpjpq/HD9YfJojG4eJuPZ9Nq4Gt3dGvPZdHE7OjcWnVHnsHcu + GMbidO8hg1S384Vw2IQVOZLgUQA1kbZpuRdm/8IePFr20HWHltPp2T1z4C1blRCSFB3riAjVfpuy + UnDYPm22XpslcUK1fZSkYjTK0AlnlBVUsG3Q8r5SawRpgHAoxz80BCtwo6lyzAc6a2lki6HaC9u0 + zbZAjxACwoM1ZiQmWM33oIAS5Gk5K0yVJoZGDBPeoFoxHMtEoKu3uPsX8mP4Dxyavmt5zrJc5J0c + 0A/VEKgVrycGEsi1CLr1TZC137gIJbT3CUmJuLTMd1kccywuzUokgOLN4QKDpqnq4Px7cl1VArUq + VkBZXVpqQ87wmmSFkmjDLRCZPAwqriQTxIT9f8NydPpeHN3W8gJ3EOF5IbACUtP5olntELTr7r0V + oNLTvsgtKqq42rurR10FyaTBKkv01T9RSr2q/AfF9Fq8EVP5VIdnu9+TvyXqVwYAAA== + headers: + content-encoding: [gzip] + content-length: ['676'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: '{"amount": 1061, "meta": {"state": "new", "participant_id": 4}, "description": + "bob"}' + headers: {} + method: POST + uri: https://api.balancedpayments.com:443/cards/CC6PZQCKGZAQsuwLSTBbRqId/card_holds + response: + body: {string: !!python/unicode "{\n \"errors\": [\n {\n \"status\": + \"Payment Required\",\n \"category_code\": \"card-declined\",\n \"additional\": + \"Account Frozen\",\n \"status_code\": 402,\n \"category_type\": + \"banking\",\n \"extras\": {},\n \"request_id\": \"OHMe1ca5954165411e4ac8902b12035401b\",\n + \ \"description\": \"R758: Account Frozen. Your request id is OHMe1ca5954165411e4ac8902b12035401b.\"\n + \ }\n ]\n}"} + headers: + content-length: ['387'] + content-type: [application/json] + status: {code: 402, message: PAYMENT REQUIRED} +- request: + body: '{"meta": {"seq": 4}}' + headers: {} + method: POST + uri: https://api.balancedpayments.com:443/customers + response: + body: {string: !!python/unicode "{\n \"customers\": [\n {\n \"name\": + null,\n \"links\": {\n \"source\": null,\n \"destination\": + null\n },\n \"updated_at\": \"2014-07-28T12:44:15.686124Z\",\n \"created_at\": + \"2014-07-28T12:44:15.567519Z\",\n \"dob_month\": null,\n \"id\": + \"CU6SMkCwTyQHAjXzmtSm0CRO\",\n \"phone\": null,\n \"href\": \"/customers/CU6SMkCwTyQHAjXzmtSm0CRO\",\n + \ \"merchant_status\": \"no-match\",\n \"meta\": {\n \"seq\": + \"4\"\n },\n \"dob_year\": null,\n \"address\": {\n \"city\": + null,\n \"line2\": null,\n \"line1\": null,\n \"state\": + null,\n \"postal_code\": null,\n \"country_code\": null\n },\n + \ \"business_name\": null,\n \"ssn_last4\": null,\n \"email\": + null,\n \"ein\": null\n }\n ],\n \"links\": {\n \"customers.source\": + \"/resources/{customers.source}\",\n \"customers.card_holds\": \"/customers/{customers.id}/card_holds\",\n + \ \"customers.bank_accounts\": \"/customers/{customers.id}/bank_accounts\",\n + \ \"customers.debits\": \"/customers/{customers.id}/debits\",\n \"customers.destination\": + \"/resources/{customers.destination}\",\n \"customers.external_accounts\": + \"/customers/{customers.id}/external_accounts\",\n \"customers.cards\": + \"/customers/{customers.id}/cards\",\n \"customers.disputes\": \"/customers/{customers.id}/disputes\",\n + \ \"customers.transactions\": \"/customers/{customers.id}/transactions\",\n + \ \"customers.refunds\": \"/customers/{customers.id}/refunds\",\n \"customers.reversals\": + \"/customers/{customers.id}/reversals\",\n \"customers.orders\": \"/customers/{customers.id}/orders\",\n + \ \"customers.credits\": \"/customers/{customers.id}/credits\"\n }\n}"} + headers: + content-length: ['1619'] + content-type: [application/json] + status: {code: 201, message: CREATED} +- request: + body: '{"expiration_month": 12, "number": "4242424242424242", "expiration_year": + 2020}' + headers: {} + method: POST + uri: https://api.balancedpayments.com:443/cards + response: + body: {string: !!python/unicode "{\n \"cards\": [\n {\n \"links\": {\n + \ \"customer\": null\n },\n \"fingerprint\": \"1e2c425a579a1c7f27a037f0662b855035ceadbff18a0adbe785fb07e2afe1cc\",\n + \ \"cvv_result\": null,\n \"number\": \"xxxxxxxxxxxx4242\",\n \"avs_postal_match\": + null,\n \"expiration_month\": 12,\n \"meta\": {},\n \"id\": + \"CC6TLLgPxyk72mp6wYSQ54YF\",\n \"category\": \"other\",\n \"type\": + \"credit\",\n \"cvv_match\": null,\n \"bank_name\": \"\",\n \"avs_street_match\": + null,\n \"brand\": \"Visa\",\n \"updated_at\": \"2014-07-28T12:44:16.442981Z\",\n + \ \"address\": {\n \"city\": null,\n \"line2\": null,\n + \ \"line1\": null,\n \"state\": null,\n \"postal_code\": + null,\n \"country_code\": null\n },\n \"can_debit\": true,\n + \ \"name\": null,\n \"expiration_year\": 2020,\n \"cvv\": null,\n + \ \"is_verified\": true,\n \"avs_result\": null,\n \"can_credit\": + false,\n \"href\": \"/cards/CC6TLLgPxyk72mp6wYSQ54YF\",\n \"created_at\": + \"2014-07-28T12:44:16.442979Z\"\n }\n ],\n \"links\": {\n \"cards.card_holds\": + \"/cards/{cards.id}/card_holds\",\n \"cards.customer\": \"/customers/{cards.customer}\",\n + \ \"cards.disputes\": \"/cards/{cards.id}/disputes\",\n \"cards.debits\": + \"/cards/{cards.id}/debits\"\n }\n}"} + headers: + access-control-allow-headers: [Content-Type] + access-control-allow-methods: ['POST, OPTIONS'] + access-control-allow-origin: ['*'] + content-length: ['1236'] + content-type: [application/json] + status: {code: 201, message: CREATED} +- request: + body: '{"links": {"customer": "/customers/CU6SMkCwTyQHAjXzmtSm0CRO"}, "cvv_result": + null, "number": "xxxxxxxxxxxx4242", "avs_postal_match": null, "expiration_month": + 12, "meta": {}, "id": "CC6TLLgPxyk72mp6wYSQ54YF", "category": "other", "type": + "credit", "cvv_match": null, "bank_name": "", "avs_street_match": null, "brand": + "Visa", "updated_at": "2014-07-28T12:44:16.442981Z", "fingerprint": "1e2c425a579a1c7f27a037f0662b855035ceadbff18a0adbe785fb07e2afe1cc", + "can_debit": true, "customer": null, "name": null, "expiration_year": 2020, + "cvv": null, "is_verified": true, "avs_result": null, "can_credit": false, "created_at": + "2014-07-28T12:44:16.442979Z", "address": {"city": null, "line2": null, "line1": + null, "state": null, "postal_code": null, "country_code": null}}' + headers: {} + method: PUT + uri: https://api.balancedpayments.com:443/cards/CC6TLLgPxyk72mp6wYSQ54YF + response: + body: + string: !!binary | + H4sIAAAAAAAAA41UTW/bMAy991cYPq+JrPoruRUBhh06bF2yYe1QGLJEJ1ps2ZDlNF7g/z7JH4mT + pth8MGxSj3wkn3i4sSybEslKe2790j+WdWjf2pxysTXmwWBOVqXKM5Daai+++8vP28Xrqn78dP/7 + 559MLTO0+PbF7vHNhyFQwsUaZCG5UAboAKYu9ogXzIhDgwQHBN0FCfJ9HIeeh+48CoTFSeKEBOkP + CEIviVEAmCTgUGofA9PdLpJQVqmJK6o0PXpElcUdzf3ocbGLT2iyK6MiLxVJo4wourmMAfuCS6J4 + LqIsF8r4HXzMkIEipjmnMjlr27LwVw8P66/7ehvgrPBfn5aPnvv0ccSaKFjnsjanc7XRNI9BVV2A + MVMJjKvzQq9yjInYRoJkLeq8tFJJAHW9tFgS0bL9wUtyglUF09xYRNo5YeS4tyi4xeHKwXPXnTv+ + JMTYReHzKBNjegKXMuHKVDeeSKcnwNfNzluznosyVV0E6QdGc3bFSfNKKFlHI+8bMVIiIgax7u7c + UrKCk2S6Lp7JaCSBGoiRPUYYjfV3wdDmZbQDyRMOpr9nCYzerqvVcOpHPrcSkpYnVhsJiQ5kT9tL + Ov0PdUn4xwxdF8+C2XN3TxvdoBdT0MV175bCxGSNNnna7oeBxKHlMuGsaVn1/q4rA260KKbD1iin + PXIwNL2OehDjZVEpeCfV0XuWqB3le4jOp4trbpq/Q/Cq6eoEAAA= + headers: + content-encoding: [gzip] + content-length: ['551'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: '{"expiration_month": 12, "number": "4242424242424242", "expiration_year": + 2030}' + headers: {} + method: POST + uri: https://api.balancedpayments.com:443/cards + response: + body: {string: !!python/unicode "{\n \"cards\": [\n {\n \"links\": {\n + \ \"customer\": null\n },\n \"fingerprint\": \"d34d613121ae61ae732d86eaf09929617bd3f5ccc60f7364d32ae1afc5f81745\",\n + \ \"cvv_result\": null,\n \"number\": \"xxxxxxxxxxxx4242\",\n \"avs_postal_match\": + null,\n \"expiration_month\": 12,\n \"meta\": {},\n \"id\": + \"CC6V7qZ6DESWZ76Ta29niOlD\",\n \"category\": \"other\",\n \"type\": + \"credit\",\n \"cvv_match\": null,\n \"bank_name\": \"\",\n \"avs_street_match\": + null,\n \"brand\": \"Visa\",\n \"updated_at\": \"2014-07-28T12:44:17.648341Z\",\n + \ \"address\": {\n \"city\": null,\n \"line2\": null,\n + \ \"line1\": null,\n \"state\": null,\n \"postal_code\": + null,\n \"country_code\": null\n },\n \"can_debit\": true,\n + \ \"name\": null,\n \"expiration_year\": 2030,\n \"cvv\": null,\n + \ \"is_verified\": true,\n \"avs_result\": null,\n \"can_credit\": + false,\n \"href\": \"/cards/CC6V7qZ6DESWZ76Ta29niOlD\",\n \"created_at\": + \"2014-07-28T12:44:17.648338Z\"\n }\n ],\n \"links\": {\n \"cards.card_holds\": + \"/cards/{cards.id}/card_holds\",\n \"cards.customer\": \"/customers/{cards.customer}\",\n + \ \"cards.disputes\": \"/cards/{cards.id}/disputes\",\n \"cards.debits\": + \"/cards/{cards.id}/debits\"\n }\n}"} + headers: + access-control-allow-headers: [Content-Type] + access-control-allow-methods: ['POST, OPTIONS'] + access-control-allow-origin: ['*'] + content-length: ['1236'] + content-type: [application/json] + status: {code: 201, message: CREATED} +- request: + body: '{"links": {"customer": "/customers/CU6SMkCwTyQHAjXzmtSm0CRO"}, "cvv_result": + null, "number": "xxxxxxxxxxxx4242", "avs_postal_match": null, "expiration_month": + 12, "meta": {}, "id": "CC6V7qZ6DESWZ76Ta29niOlD", "category": "other", "type": + "credit", "cvv_match": null, "bank_name": "", "avs_street_match": null, "brand": + "Visa", "updated_at": "2014-07-28T12:44:17.648341Z", "fingerprint": "d34d613121ae61ae732d86eaf09929617bd3f5ccc60f7364d32ae1afc5f81745", + "can_debit": true, "customer": null, "name": null, "expiration_year": 2030, + "cvv": null, "is_verified": true, "avs_result": null, "can_credit": false, "created_at": + "2014-07-28T12:44:17.648338Z", "address": {"city": null, "line2": null, "line1": + null, "state": null, "postal_code": null, "country_code": null}}' + headers: {} + method: PUT + uri: https://api.balancedpayments.com:443/cards/CC6V7qZ6DESWZ76Ta29niOlD + response: + body: + string: !!binary | + H4sIAAAAAAAAA41U32+bMBB+71+BeG4TbBxD8lalk/YyVVuydso0Icc2jRcwzDZZs4j/fTY/EpKm + 05BA4s7f3Xd3n+9w43k+JYppf+Z9tz+ed2i+1pwJuXXm3uBOVtoUOVfW6s+/4sWn7fz3cv/54/3P + b39ys8iD+ZdHv8PXt32gVMgXrkolpHFAFiKGQQggIBzbNwohizEnaTCdwikG0ZqF6YRSioM0CjFi + ISQckJRO0hhEaOIfA9PdLlFcV5mLK6ssO3pkla9bmq+DB0EET2iy00lZaEOyJCeGbi5j8NdSKGJE + IZO8kMb5ATxmyLkhrjmnMgVr2jLHT9GvFX74sHheRXhJ4FSKx+xhwJoY/lKovTtdmI2leQxq9iV3 + Zqo4E+a80Ksc10RuE0nyBnVemjaKc3O9tLUismH7JDQ5waqSWW4sIc2cYADQXRDdwXgJ4AyhGYhH + QYQRRqtBJsbsBC5lIoyrbjiRVk8cXjeDt2Y7F+OqugjSDYwW7IqTFpU0ap8MvG/ESIlMGF/b7s48 + oyp+kkzbxTMZDSSw58TJHgZhMNTfBUNf6GTHlUgFd/09S+D0dl2tjlM38pmXkkyfWG0UT20gf9xc + 0vF/qEvxf84wGmEUh2G8au9pbRv0wxV0cd3bpTByWZNNkTX7oSdxaLiMBKsbVp2/7UqPGyyKcb81 + 9LhD9oa601EHYkKXleHvpDp6zxI1o3wP0fpscfVN/RfqZT+Q6gQAAA== + headers: + content-encoding: [gzip] + content-length: ['553'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: null + headers: {} + method: GET + uri: https://api.balancedpayments.com:443/customers/CU6SMkCwTyQHAjXzmtSm0CRO + response: + body: + string: !!binary | + H4sIAAAAAAAAA41UwY7TMBC971dEOdNNE6XdpTfUCxeEYBcJgVA0tY1imtjFdoBS5d8Zt0nq2gFz + yWHmvWfPmxef7pIkJZ02smVKp5vkMxaS5HT+YktAy7AquqZ5MdYaLvYWOoIQpmWniA/EOmXacAGG + SzGoDCL9pNYdKBhGKzCISItlXi6WD4vi8TkvNmW5yVf368d1XpSf0olCFItQVuuHVf7SoVC5q1op + TO0Pw6k9dvth/fRmv/35fHz3+tW3j79b89Qut+/fXs881FL4A6a1Yl8tPZsczOJKaDSpQZhKGzCd + NTIVctGCIfX1uJYZ8Cxm3y20TAMH7WxHBsofDShVTHubItwcPSDuCVfKivlyHpbtxX0vUOQgsdFU + RNKZJpGdMOrodoNBdp3Ge2hdzaVOa1E1oE3pj8la4E1Q5DeB6/GsLzY/dlInvNfo308RTjN07Zxn + nZ2mxQ79fliRQySgaFXLhp5X6UTBIXPaZw7ukmRHYwdiXwE5mxSRuYUGSpTteExiwMxw3b/1Lz44 + v/SMGeyXYUpgCv5vmBAeXMraFrHkAgmYlOtDZ1iEPKECvlEgNBD7eEU0bpCBDj4SnYjNMIJm2D/w + ZYYmcgXFRligIBW9vO3/COeACbj41NJonkYQ/l/9Xf8HeuokgVMGAAA= + headers: + content-encoding: [gzip] + content-length: ['497'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: null + headers: {} + method: GET + uri: https://api.balancedpayments.com:443/customers/CU6SMkCwTyQHAjXzmtSm0CRO/cards?limit=10&offset=0 + response: + body: + string: !!binary | + H4sIAAAAAAAAA+VWW2/TMBR+36+o8sATa2MnsdNKE0IdiIchGC2DFaHI8WU1zaU4Ttcy9b9jJ2mb + dp2GQGgSVGrUnpNzfC7f+Y7vTjodhxLFCmfQ+WL+dDp31dOIE5nNrHgjsG+Whc5TrozUGX5Eo7ez + 4e14dfnm5bfPP1I9St3hh3dOY79+vnEkZHbD1VzJTFtDwCH1YUAC3CeAYgExcT0sXIRgHAaB6wWU + ExYLAULimh8ch4GIXcwhERxQ6mwd08UiUrwoE+s3K5Nkq8nKNK7DXLY+PvThzposimieF5okUUo0 + nR764Mu5VETLPIvSPNNWD+D2hJRrYouzS1OyqixDNL64uHm/XM0wTOfo9np0GfjXr1tRE81vcrWy + b+d6asLcOtWrObdiqjiTej/RozHGJJtFGUkrq/3UCq0418dTixXJqmivZEF2ZuWcmdhYRKo+QRf4 + py4+heEYwIHvDwDqhhD6bjhpncSY6cAhTKS22bU7UuOJw+NicF9s+qJtVgdOmobRnB1R0rzMtFpF + Le09MFKSRYzHprqDjlYl30GmruIejFoQWHFiYQ9d6LbxdxChI4towZUUktv67h1g8XYcrTampuWD + jiBJsYtqqrgwjpxeNaS9X0CX4o/00PdhH/cn9Zw28P3rQ888nyHgAQgIR+aLPchCxIlw+33YRwDH + zBMBpRS5AnvIZx4kHBBBAxEC7Af7s3C8jE869Ff4+wSdvxp9mmA0JrCfyXfJ+b8w9GHXxchH/n89 + 9N4TD/1j6Hpk6HEX+aHnGeKu+HBtnl8tizmbLVaJnYQUFfP3NnveEM4DW77moxeJTKU+A+6zXIiC + 6zO3QbyT8eX+UnZ2TNZcIn7feXWqCRQ0XXHq041kI5grvpB5affSjtAdnZtlb0m8ZnBHSPXnCZvS + VSR6cF+qb1Xd9o2pVda7is632vWmbLWYyWJeam6j3xB/YyDZurfVNlk0rozDaJon1U3uiJV9q9Hv + 2VW78AGbRmczPFn/BA+kcscrCgAA + headers: + content-encoding: [gzip] + content-length: ['762'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: '{"meta": {"seq": 5}}' + headers: {} + method: POST + uri: https://api.balancedpayments.com:443/customers + response: + body: {string: !!python/unicode "{\n \"customers\": [\n {\n \"name\": + null,\n \"links\": {\n \"source\": null,\n \"destination\": + null\n },\n \"updated_at\": \"2014-07-28T12:44:19.427386Z\",\n \"created_at\": + \"2014-07-28T12:44:19.315286Z\",\n \"dob_month\": null,\n \"id\": + \"CU6X0aCm4qom2lpUvyxszYvv\",\n \"phone\": null,\n \"href\": \"/customers/CU6X0aCm4qom2lpUvyxszYvv\",\n + \ \"merchant_status\": \"no-match\",\n \"meta\": {\n \"seq\": + \"5\"\n },\n \"dob_year\": null,\n \"address\": {\n \"city\": + null,\n \"line2\": null,\n \"line1\": null,\n \"state\": + null,\n \"postal_code\": null,\n \"country_code\": null\n },\n + \ \"business_name\": null,\n \"ssn_last4\": null,\n \"email\": + null,\n \"ein\": null\n }\n ],\n \"links\": {\n \"customers.source\": + \"/resources/{customers.source}\",\n \"customers.card_holds\": \"/customers/{customers.id}/card_holds\",\n + \ \"customers.bank_accounts\": \"/customers/{customers.id}/bank_accounts\",\n + \ \"customers.debits\": \"/customers/{customers.id}/debits\",\n \"customers.destination\": + \"/resources/{customers.destination}\",\n \"customers.external_accounts\": + \"/customers/{customers.id}/external_accounts\",\n \"customers.cards\": + \"/customers/{customers.id}/cards\",\n \"customers.disputes\": \"/customers/{customers.id}/disputes\",\n + \ \"customers.transactions\": \"/customers/{customers.id}/transactions\",\n + \ \"customers.refunds\": \"/customers/{customers.id}/refunds\",\n \"customers.reversals\": + \"/customers/{customers.id}/reversals\",\n \"customers.orders\": \"/customers/{customers.id}/orders\",\n + \ \"customers.credits\": \"/customers/{customers.id}/credits\"\n }\n}"} + headers: + content-length: ['1619'] + content-type: [application/json] + status: {code: 201, message: CREATED} +- request: + body: null + headers: {} + method: GET + uri: https://api.balancedpayments.com:443/customers/CU6X0aCm4qom2lpUvyxszYvv + response: + body: + string: !!binary | + H4sIAAAAAAAAA41UXW+jMBB8769APDclUPqV1/6Fq3TtqUIb2yesGpvaJmou4r93nQAhNne+Fx52 + Z8be2cGHqyRJSWesapg26Sb5hYUkORy/2JLQMKzKTojrsSa4/HDQEYQwozpNfCDWKTOWS7BcyUFl + EOknta6lYBmtwCIiLdZ5uVo/rIrHH3mxKctN/nRTFg+3j/dv6UQhmkUot/ldcUGhals1StraH4ZT + d+zzy/3PNTw35adqCtG+7PZf5s/rbnc+s62V9AdMa81+O3o2OZjFldBoUoO0lbFgO2dkKtWqAUvq + 83ENs+BZzD4d9C4NHHSz7RlofzSgVDPjbYpwu/eAuCdcKSuWy3lYdhf3vUCRVmFDVETRhSZRnbR6 + P+8Gg2w7g/cwplpKnTGyEmBs6Y/JGuAiKPKLwPV41rvLj5t0Ft5z9G+mCKcZunbMs8kO02KHfj+s + aEYkoGlVK0GPq5xFYUbmtM9muFOSZxpbkB8VkKNJEZlLaKBE2ZbHJAbMAnf+t/7Fh9kvvWAG+7JM + S0zB/w0TwoNLOdsilpwgAZNy03aWRcgTKuBbDdIAcY9XROMCGejgI9HJ2AwjaIG9w5cZROQKmo2w + QEFpenrb/xHOARNw8aml0TyNIPy/+qv+G5NozWlTBgAA + headers: + content-encoding: [gzip] + content-length: ['489'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: null + headers: {} + method: GET + uri: https://api.balancedpayments.com:443/customers/CU6X0aCm4qom2lpUvyxszYvv/cards?limit=10&offset=0 + response: + body: {string: !!python/unicode "{\n \"cards\": [],\n \"meta\": {\n \"last\": + \"/customers/CU6X0aCm4qom2lpUvyxszYvv/cards?limit=10&offset=0\",\n \"next\": + null,\n \"href\": \"/customers/CU6X0aCm4qom2lpUvyxszYvv/cards?limit=10&offset=0\",\n + \ \"limit\": 10,\n \"offset\": 0,\n \"previous\": null,\n \"total\": + 0,\n \"first\": \"/customers/CU6X0aCm4qom2lpUvyxszYvv/cards?limit=10&offset=0\"\n + \ },\n \"links\": {}\n}"} + headers: + content-length: ['364'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: null + headers: {} + method: GET + uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd + response: + body: + string: !!binary | + H4sIAAAAAAAAA41Uy27bMBC85ysEnWvLipQ29i1oby2CAG3RokUh0CQLEZFIhY+ggqF/79KWZJpU + wlx02J0ZcmdHPFwlSYqN0qKlUqW75DcUkuRw/EKLo5ZClZumeTfVGsYfLXQCAUwJI7EPhDqhSjOO + NBN8VBlFhlnNdARpSiqkAZFeb/Jytfmw2uTf8ptdWezKcp0Xxfvb7a90pmBJX6UU622+uSlKh0LE + vmoF17U/DCP22I/fi88/Sf98//UT7b88FN3dD7mtn8j5zK4W3B8wrSX9a+nZ7GAWVwKjcY24rpRG + 2lgjUy5WLdK4Ph/XUo08i+mTheZp4KCdradI+qMhQiRV3qYw070HhD3BSun1cjkPy/bivhcg0glo + NBUWZKGJheFa9m43GGRvFNxDqWopdUrxqkFKl/6YtEWsCYrsInADnPXH5sdO6oT3HP31HOE0A9eO + eVbZYV7s2B/GFTlEjCSpatGQ4yqdKDhkRobMwZ2S7GjsEX+sED6aFJG5hAZKhO5ZTGLELHDdv/UF + H5xfesEM+k9TySEFbxsmhAeXsrZFLDlBAiZhqjOaRsgzKuBribhC2D5eEY0LZKADj4ThsRkm0AL7 + GV5m1ESuIOkECxSEJKe3/ZVwjpiAC08tieZpAsH/NVwN/wHQiK4AUwYAAA== + headers: + content-encoding: [gzip] + content-length: ['499'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: null + headers: {} + method: GET + uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd/cards?limit=10&offset=0 + response: + body: + string: !!binary | + H4sIAAAAAAAAA6VVW2+bMBR+76+weNhTmwBOyEWqprSbVq1NWq1dO3WakIPtxiqxmW2yoCr/fbaB + AL1o08YDEud+vvOdw9MBAF6CJFbeFHw3HwA8ubcRp4w/WnEtsJa50mJNpJF6p1/h+TdcbBbXH0hx + cQWz2Z2crH5ir/LfHdaBKOMPRGaScW0dx8mI+oMQwmiI6HicQAKjhPrhaBCRBEUDihMKI4yTkAQU + Tow5SSYUUor9IQqjkbcPnGw2sSQqT21cnqfpXsPz9bIsc9t6AvM03mij4kwojdJ4jXSysrVx0ejJ + NmMSaSZ4vBZcW33g7zOsiUZdcCR5MLYOGsFpruxHbW6xY7qItfjlLNoKhLFpQsWhVZyILQhfQsiw + i3sK52cn6i7anKELqPKLW3xzu7yctBBBmjwIWVhroVcGgn3BusiIFSeSYKa7INb9dzBcIv4Yc7R2 + Xp+v5pdfPs0W4PRsdv0RnMwW54dg0Zv1ungqLQnRDZ4FUY3BUiLu+rhlCjXiPMOmahwjx47QDwZH + /ujID26C4XQAp4NhbzwMQhjct1KVkBn7NjkNwEbS7qFkMbHIviIObD4TGcwR4+Dald4ejKGGds2/ + Os+KOonAzmbih4Hf9k5EzrUs4srA5n+xGQniMSZLM44p0DInDX9L2DvzaPGxIMjuYOiHDSHNMjxr + 02Mq3hDJKCMW9k4CS/796nhl76BiIjBjAmV7wFYPsABcaOBY0hq4rb5i0xRQlKqm/pUk1KLSd7el + /xfEleQPJBhEcBLA+3I3dgbKHxYtr7OHXoqUY1G/vlQm9xt3qiztfcrWTB8H/jtBqSL6uJ6hx8m2 + e1a8pqnqDP57cJfVFFofFK/MbiTVQL1Mkg0TuT3ADQs8Lcy5sn4lUzzK5P83bGB0x/rZxS//Cz3M + VJZrs8fNOJ/cVHsM7/p7bVVQqWn/J1qjqPxq7a5alyqRdY1XInX/opo5rVQtfSeZW6A3fCqd7fBg + 9xv4QHhu7QYAAA== + headers: + content-encoding: [gzip] + content-length: ['751'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: '{"amount": 1000, "meta": {"state": "new", "participant_id": 2}, "description": + "janet"}' + headers: {} + method: POST + uri: https://api.balancedpayments.com:443/cards/CC3MHBsW6vHaL3suLVdTVbO9/card_holds + response: + body: {string: !!python/unicode "{\n \"card_holds\": [\n {\n \"status\": + \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"card\": + \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \"debit\": null\n },\n \"updated_at\": + \"2014-07-28T12:44:21.775480Z\",\n \"created_at\": \"2014-07-28T12:44:21.559915Z\",\n + \ \"transaction_number\": \"HL101-260-3999\",\n \"expires_at\": \"2014-08-04T12:44:21.689475Z\",\n + \ \"failure_reason\": null,\n \"currency\": \"USD\",\n \"amount\": + 1000,\n \"meta\": {\n \"state\": \"new\",\n \"participant_id\": + \"2\"\n },\n \"href\": \"/card_holds/HL6ZwcjcM9CPa5eZEgQFeQ0g\",\n + \ \"failure_reason_code\": null,\n \"voided_at\": null,\n \"id\": + \"HL6ZwcjcM9CPa5eZEgQFeQ0g\"\n }\n ],\n \"links\": {\n \"card_holds.events\": + \"/card_holds/{card_holds.id}/events\",\n \"card_holds.card\": \"/cards/{card_holds.card}\",\n + \ \"card_holds.debits\": \"/card_holds/{card_holds.id}/debits\",\n \"card_holds.debit\": + \"/debits/{card_holds.debit}\"\n }\n}"} + headers: + content-length: ['944'] + content-type: [application/json] + status: {code: 201, message: CREATED} +- request: + body: '{"status": "succeeded", "transaction_number": "HL101-260-3999", "description": + "janet", "amount": 1000, "created_at": "2014-07-28T12:44:21.559915Z", "card_href": + "/cards/CC3MHBsW6vHaL3suLVdTVbO9", "updated_at": "2014-07-28T12:44:21.775480Z", + "expires_at": "2014-08-04T12:44:21.689475Z", "failure_reason": null, "currency": + "USD", "is_void": true, "meta": {"state": "cancelled", "participant_id": "2"}, + "debit": null, "failure_reason_code": null, "voided_at": null, "id": "HL6ZwcjcM9CPa5eZEgQFeQ0g"}' + headers: {} + method: PUT + uri: https://api.balancedpayments.com:443/card_holds/HL6ZwcjcM9CPa5eZEgQFeQ0g + response: + body: {string: !!python/unicode "{\n \"card_holds\": [\n {\n \"status\": + \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"card\": + \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \"debit\": null\n },\n \"updated_at\": + \"2014-07-28T12:44:22.144977Z\",\n \"created_at\": \"2014-07-28T12:44:21.559915Z\",\n + \ \"transaction_number\": \"HL101-260-3999\",\n \"expires_at\": \"2014-08-04T12:44:21.689475Z\",\n + \ \"failure_reason\": null,\n \"currency\": \"USD\",\n \"amount\": + 1000,\n \"meta\": {\n \"state\": \"cancelled\",\n \"participant_id\": + \"2\"\n },\n \"href\": \"/card_holds/HL6ZwcjcM9CPa5eZEgQFeQ0g\",\n + \ \"failure_reason_code\": null,\n \"voided_at\": \"2014-07-28T12:44:22.144979Z\",\n + \ \"id\": \"HL6ZwcjcM9CPa5eZEgQFeQ0g\"\n }\n ],\n \"links\": {\n + \ \"card_holds.events\": \"/card_holds/{card_holds.id}/events\",\n \"card_holds.card\": + \"/cards/{card_holds.card}\",\n \"card_holds.debits\": \"/card_holds/{card_holds.id}/debits\",\n + \ \"card_holds.debit\": \"/debits/{card_holds.debit}\"\n }\n}"} + headers: + content-length: ['975'] + content-type: [application/json] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/py/fixtures/TestCredits.yml b/tests/py/fixtures/TestCredits.yml new file mode 100644 index 0000000000..1a72d27541 --- /dev/null +++ b/tests/py/fixtures/TestCredits.yml @@ -0,0 +1,71 @@ +interactions: +- request: + body: null + headers: {} + method: GET + uri: https://api.balancedpayments.com:443/customers/CU3O2IKYocPZ5LA7GkZWpwKZ + response: + body: + string: !!binary | + H4sIAAAAAAAAA41UTY/TMBS876+IcqabpJu2orcVB4QWCQ4gRBGKXNsoVhM7+AOoqvx3ntMk69oB + 7yWH55mJ37zxu9wlSYqN0qKlUqX75BsUkuQyfOGIo5ZClZumeTXVGsZPFjqBAKaEkdgHQp1QpRlH + mgk+qowi/axmOoI0JRXSgEjXeVGu8t0qLz4Vm335sC+397tNmW/zQzpTsKQRynazLnavHQoRx6oV + XNd+M4zY3775/PBh/e7pq8AfD5v3j7u3p8OX7veTI9DVgvsNprWkPyw9mx3M4kpgNK4R15XSSBtr + ZMrFqkUa188ttlQjz2L6czAoDRy0vZ0pkn5riBBJlTcpzPTZA8KcYKR0vVwuwrK9uO8FiHQCDpoK + C7JwiIXhWp7d06CRo1FwD6WqpdQpxasGKV36bdIWsSYospvA9fCv7zY/tlMnvM/Rv58jnGbg2pBn + lV3mwY7n/Tgih4iRJFUtGjKM0omCQ2akzxzcNcmOxhHxU4XwYFJE5hYaKBF6ZDGJEbPAdV/rP3xw + nvSCGfSPppJDCl7WTAgPLmVti1hyhQRMwlRnNI2QZ1TA1xJxhbBdXhGNG2SgA0vC8FgPE2iB/Qs2 + M2oiV5B0ggUKQpLrbv9POEdMwIVVS6J5mkDwvvq7/i9xeE/EUwYAAA== + headers: + content-encoding: [gzip] + content-length: ['496'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: null + headers: {} + method: GET + uri: https://api.balancedpayments.com:443/customers/CU3O2IKYocPZ5LA7GkZWpwKZ/bank_accounts?limit=10&offset=0 + response: + body: + string: !!binary | + H4sIAAAAAAAAA61UXW+bMBR9769APOxpDTYfIUSqpnTqsi1R232pW6YJObZpvBBIjWkTRfz32Q4E + SIPWh74gcc/1veeec3V3Z4ZhzlGyDBHGaZ6IzBwav2XQMHb6K2Ge5oIl92GSr+aUS9yENgSuPRjY + 5tsqSddI0Ioq/O5qOv1mfBh9Hd8Yl6PriXE9qjPLRqHYrnUyXlC8lPXrjKrMx3RFufEZbWsoZslS + UazISXo4z4RKVLXe/3Bu7E+TXym+nXnTkT9ezu7WT5PZocDRtOEj5SxiGAmWJrJAksdxOVFxGA2j + JMScEiZkhuA5rRFOkaAkRAoxbQDdc+CfA/gdekPXGbpBD/j9gefV/c1ITkr5mrNEvyEI9z3okoAQ + B0FMfIQD2yOe7wYUOGCAbYlHyPaxB/qIRnOAg7kN+xBGURD0g1qZfE3+w8UDvgeaXBacRoqE1VoA + 63LkfJkm2dX4nmd/H6LFz4Xr3D64pO61ogIpE2qNKlPrHdlsNrbjoIbvhHCaHZvHxLbUvVJVWiRd + pvbpMHwezoQc/Hl4nUogDnFKToB61/m2iZ70ndC5tj1CcVb7zoiSrVMnXamQ3z9qJrNSS4fNGGXa + eava28zq2tq2L+9itmLiAoI3aRRlVFyAUlszoRtVUi3vXkTz4OzrNdHdZRcIyh57FjJSBdacPrI0 + Vw43qIhUuqDelc8ixl9PACmp3kG1MY270L5oPW2ionW86bvW4vcYKawytyTbxpt/rcvxwtrNY5NV + 5rVbNI9ZY0OOiFZZRUcRfaxeSGp/2TrodE6sxWzNYx1x7HxamMq1s+IftGquiHsGAAA= + headers: + content-encoding: [gzip] + content-length: ['620'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: '{"amount": 1900.00, "description": "bob"}' + headers: {} + method: POST + uri: https://api.balancedpayments.com:443/bank_accounts/BA3QLnsEGgrsjqfhXh43Pq4d/credits + response: + body: {string: !!python/unicode "{\n \"credits\": [\n {\n \"status\": + \"succeeded\",\n \"description\": \"bob\",\n \"links\": {\n \"customer\": + \"CU3O2IKYocPZ5LA7GkZWpwKZ\",\n \"destination\": \"BA3QLnsEGgrsjqfhXh43Pq4d\",\n + \ \"order\": null\n },\n \"updated_at\": \"2014-07-01T19:19:43.494105Z\",\n + \ \"created_at\": \"2014-07-01T19:19:43.018298Z\",\n \"transaction_number\": + \"CR807-645-5056\",\n \"failure_reason\": null,\n \"currency\": + \"USD\",\n \"amount\": 1900,\n \"failure_reason_code\": null,\n + \ \"meta\": {},\n \"href\": \"/credits/CR56p7y5a2SbEJCkNqZUWXS9\",\n + \ \"appears_on_statement_as\": \"example.com\",\n \"id\": \"CR56p7y5a2SbEJCkNqZUWXS9\"\n + \ }\n ],\n \"links\": {\n \"credits.order\": \"/orders/{credits.order}\",\n + \ \"credits.customer\": \"/customers/{credits.customer}\",\n \"credits.destination\": + \"/resources/{credits.destination}\",\n \"credits.reversals\": \"/credits/{credits.id}/reversals\",\n + \ \"credits.events\": \"/credits/{credits.id}/events\"\n }\n}"} + headers: + content-length: ['953'] + content-type: [application/json] + status: {code: 201, message: CREATED} +version: 1 diff --git a/tests/py/fixtures/TestPayday.yml b/tests/py/fixtures/TestPayday.yml index 83db00b89d..36b6be7bd9 100644 --- a/tests/py/fixtures/TestPayday.yml +++ b/tests/py/fixtures/TestPayday.yml @@ -48,30 +48,211 @@ interactions: content-length: ['751'] content-type: [application/json] status: {code: 200, message: OK} +- request: + body: '{"amount": 1000, "meta": {"state": "new", "participant_id": 2}, "description": + "janet"}' + headers: {} + method: POST + uri: https://api.balancedpayments.com:443/cards/CC3MHBsW6vHaL3suLVdTVbO9/card_holds + response: + body: {string: !!python/unicode "{\n \"card_holds\": [\n {\n \"status\": + \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"card\": + \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \"debit\": null\n },\n \"updated_at\": + \"2014-07-28T12:45:10.156500Z\",\n \"created_at\": \"2014-07-28T12:45:09.914076Z\",\n + \ \"transaction_number\": \"HL141-112-2168\",\n \"expires_at\": \"2014-08-04T12:45:10.042686Z\",\n + \ \"failure_reason\": null,\n \"currency\": \"USD\",\n \"amount\": + 1000,\n \"meta\": {\n \"state\": \"new\",\n \"participant_id\": + \"2\"\n },\n \"href\": \"/card_holds/HL4Q9bCd6Wt60Z4H8QCgJI9\",\n + \ \"failure_reason_code\": null,\n \"voided_at\": null,\n \"id\": + \"HL4Q9bCd6Wt60Z4H8QCgJI9\"\n }\n ],\n \"links\": {\n \"card_holds.events\": + \"/card_holds/{card_holds.id}/events\",\n \"card_holds.card\": \"/cards/{card_holds.card}\",\n + \ \"card_holds.debits\": \"/card_holds/{card_holds.id}/debits\",\n \"card_holds.debit\": + \"/debits/{card_holds.debit}\"\n }\n}"} + headers: + content-length: ['942'] + content-type: [application/json] + status: {code: 201, message: CREATED} +- request: + body: '{"amount": 1000, "description": "janet"}' + headers: {} + method: POST + uri: https://api.balancedpayments.com:443/card_holds/HL4Q9bCd6Wt60Z4H8QCgJI9/debits + response: + body: {string: !!python/unicode "{\n \"debits\": [\n {\n \"status\": + \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"customer\": + \"CU3KXdyvNSDeyLP3pAWr9hqd\",\n \"source\": \"CC3MHBsW6vHaL3suLVdTVbO9\",\n + \ \"order\": null,\n \"dispute\": null\n },\n \"updated_at\": + \"2014-07-28T12:45:11.072385Z\",\n \"created_at\": \"2014-07-28T12:45:10.634916Z\",\n + \ \"transaction_number\": \"W048-092-6532\",\n \"failure_reason\": + null,\n \"currency\": \"USD\",\n \"amount\": 1000,\n \"failure_reason_code\": + null,\n \"meta\": {\n \"state\": \"new\",\n \"participant_id\": + \"2\"\n },\n \"href\": \"/debits/WD5EixoE4g0Y05maT0KUnBj\",\n \"appears_on_statement_as\": + \"BAL*example.com\",\n \"id\": \"WD5EixoE4g0Y05maT0KUnBj\"\n }\n ],\n + \ \"links\": {\n \"debits.customer\": \"/customers/{debits.customer}\",\n + \ \"debits.dispute\": \"/disputes/{debits.dispute}\",\n \"debits.source\": + \"/resources/{debits.source}\",\n \"debits.order\": \"/orders/{debits.order}\",\n + \ \"debits.refunds\": \"/debits/{debits.id}/refunds\",\n \"debits.events\": + \"/debits/{debits.id}/events\"\n }\n}"} + headers: + content-length: ['1061'] + content-type: [application/json] + status: {code: 201, message: CREATED} +- request: + body: '{"status": "succeeded", "description": "janet", "amount": 1000, "created_at": + "2014-07-28T12:45:09.914076Z", "card_href": "/cards/CC3MHBsW6vHaL3suLVdTVbO9", + "updated_at": "2014-07-28T12:45:10.156500Z", "expires_at": "2014-08-04T12:45:10.042686Z", + "failure_reason": null, "currency": "USD", "transaction_number": "HL141-112-2168", + "meta": {"state": "captured", "participant_id": "2"}, "debit": null, "failure_reason_code": + null, "voided_at": null, "id": "HL4Q9bCd6Wt60Z4H8QCgJI9"}' + headers: {} + method: PUT + uri: https://api.balancedpayments.com:443/card_holds/HL4Q9bCd6Wt60Z4H8QCgJI9 + response: + body: {string: !!python/unicode "{\n \"card_holds\": [\n {\n \"status\": + \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"card\": + \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \"debit\": \"WD5EixoE4g0Y05maT0KUnBj\"\n + \ },\n \"updated_at\": \"2014-07-28T12:45:11.454240Z\",\n \"created_at\": + \"2014-07-28T12:45:09.914076Z\",\n \"transaction_number\": \"HL141-112-2168\",\n + \ \"expires_at\": \"2014-08-04T12:45:10.042686Z\",\n \"failure_reason\": + null,\n \"currency\": \"USD\",\n \"amount\": 1000,\n \"meta\": + {\n \"state\": \"captured\",\n \"participant_id\": \"2\"\n },\n + \ \"href\": \"/card_holds/HL4Q9bCd6Wt60Z4H8QCgJI9\",\n \"failure_reason_code\": + null,\n \"voided_at\": null,\n \"id\": \"HL4Q9bCd6Wt60Z4H8QCgJI9\"\n + \ }\n ],\n \"links\": {\n \"card_holds.events\": \"/card_holds/{card_holds.id}/events\",\n + \ \"card_holds.card\": \"/cards/{card_holds.card}\",\n \"card_holds.debits\": + \"/card_holds/{card_holds.id}/debits\",\n \"card_holds.debit\": \"/debits/{card_holds.debit}\"\n + \ }\n}"} + headers: + content-length: ['968'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: null + headers: {} + method: GET + uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd + response: + body: + string: !!binary | + H4sIAAAAAAAAA41Uy27bMBC85ysEnWvLipQ29i1oby2CAG3RokUh0CQLEZFIhY+ggqF/79KWZJpU + wlx02J0ZcmdHPFwlSYqN0qKlUqW75DcUkuRw/EKLo5ZClZumeTfVGsYfLXQCAUwJI7EPhDqhSjOO + NBN8VBlFhlnNdARpSiqkAZFeb/Jytfmw2uTf8ptdWezKcp0Xxfvb7a90pmBJX6UU622+uSlKh0LE + vmoF17U/DCP22I/fi88/Sf98//UT7b88FN3dD7mtn8j5zK4W3B8wrSX9a+nZ7GAWVwKjcY24rpRG + 2lgjUy5WLdK4Ph/XUo08i+mTheZp4KCdradI+qMhQiRV3qYw070HhD3BSun1cjkPy/bivhcg0glo + NBUWZKGJheFa9m43GGRvFNxDqWopdUrxqkFKl/6YtEWsCYrsInADnPXH5sdO6oT3HP31HOE0A9eO + eVbZYV7s2B/GFTlEjCSpatGQ4yqdKDhkRobMwZ2S7GjsEX+sED6aFJG5hAZKhO5ZTGLELHDdv/UF + H5xfesEM+k9TySEFbxsmhAeXsrZFLDlBAiZhqjOaRsgzKuBribhC2D5eEY0LZKADj4ThsRkm0AL7 + GV5m1ESuIOkECxSEJKe3/ZVwjpiAC08tieZpAsH/NVwN/wHQiK4AUwYAAA== + headers: + content-encoding: [gzip] + content-length: ['499'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: null + headers: {} + method: GET + uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd/cards?limit=10&offset=0 + response: + body: + string: !!binary | + H4sIAAAAAAAAA6VVW2+bMBR+76+weNhTmwBOyEWqprSbVq1NWq1dO3WakIPtxiqxmW2yoCr/fbaB + AL1o08YDEud+vvOdw9MBAF6CJFbeFHw3HwA8ubcRp4w/WnEtsJa50mJNpJF6p1/h+TdcbBbXH0hx + cQWz2Z2crH5ir/LfHdaBKOMPRGaScW0dx8mI+oMQwmiI6HicQAKjhPrhaBCRBEUDihMKI4yTkAQU + Tow5SSYUUor9IQqjkbcPnGw2sSQqT21cnqfpXsPz9bIsc9t6AvM03mij4kwojdJ4jXSysrVx0ejJ + NmMSaSZ4vBZcW33g7zOsiUZdcCR5MLYOGsFpruxHbW6xY7qItfjlLNoKhLFpQsWhVZyILQhfQsiw + i3sK52cn6i7anKELqPKLW3xzu7yctBBBmjwIWVhroVcGgn3BusiIFSeSYKa7INb9dzBcIv4Yc7R2 + Xp+v5pdfPs0W4PRsdv0RnMwW54dg0Zv1ungqLQnRDZ4FUY3BUiLu+rhlCjXiPMOmahwjx47QDwZH + /ujID26C4XQAp4NhbzwMQhjct1KVkBn7NjkNwEbS7qFkMbHIviIObD4TGcwR4+Dald4ejKGGds2/ + Os+KOonAzmbih4Hf9k5EzrUs4srA5n+xGQniMSZLM44p0DInDX9L2DvzaPGxIMjuYOiHDSHNMjxr + 02Mq3hDJKCMW9k4CS/796nhl76BiIjBjAmV7wFYPsABcaOBY0hq4rb5i0xRQlKqm/pUk1KLSd7el + /xfEleQPJBhEcBLA+3I3dgbKHxYtr7OHXoqUY1G/vlQm9xt3qiztfcrWTB8H/jtBqSL6uJ6hx8m2 + e1a8pqnqDP57cJfVFFofFK/MbiTVQL1Mkg0TuT3ADQs8Lcy5sn4lUzzK5P83bGB0x/rZxS//Cz3M + VJZrs8fNOJ/cVHsM7/p7bVVQqWn/J1qjqPxq7a5alyqRdY1XInX/opo5rVQtfSeZW6A3fCqd7fBg + 9xv4QHhu7QYAAA== + headers: + content-encoding: [gzip] + content-length: ['751'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: '{"amount": 1576, "meta": {"state": "new", "participant_id": 2}, "description": + "janet"}' + headers: {} + method: POST + uri: https://api.balancedpayments.com:443/cards/CC3MHBsW6vHaL3suLVdTVbO9/card_holds + response: + body: {string: !!python/unicode "{\n \"card_holds\": [\n {\n \"status\": + \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"card\": + \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \"debit\": null\n },\n \"updated_at\": + \"2014-07-28T12:45:12.850022Z\",\n \"created_at\": \"2014-07-28T12:45:12.631439Z\",\n + \ \"transaction_number\": \"HL977-592-8484\",\n \"expires_at\": \"2014-08-04T12:45:12.764652Z\",\n + \ \"failure_reason\": null,\n \"currency\": \"USD\",\n \"amount\": + 1576,\n \"meta\": {\n \"state\": \"new\",\n \"participant_id\": + \"2\"\n },\n \"href\": \"/card_holds/HL7TDxM5fKRKe3RqQYc6eNv\",\n + \ \"failure_reason_code\": null,\n \"voided_at\": null,\n \"id\": + \"HL7TDxM5fKRKe3RqQYc6eNv\"\n }\n ],\n \"links\": {\n \"card_holds.events\": + \"/card_holds/{card_holds.id}/events\",\n \"card_holds.card\": \"/cards/{card_holds.card}\",\n + \ \"card_holds.debits\": \"/card_holds/{card_holds.id}/debits\",\n \"card_holds.debit\": + \"/debits/{card_holds.debit}\"\n }\n}"} + headers: + content-length: ['942'] + content-type: [application/json] + status: {code: 201, message: CREATED} - request: body: '{"amount": 1576, "description": "janet"}' headers: {} method: POST - uri: https://api.balancedpayments.com:443/cards/CC3MHBsW6vHaL3suLVdTVbO9/debits + uri: https://api.balancedpayments.com:443/card_holds/HL7TDxM5fKRKe3RqQYc6eNv/debits response: body: {string: !!python/unicode "{\n \"debits\": [\n {\n \"status\": \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"customer\": \"CU3KXdyvNSDeyLP3pAWr9hqd\",\n \"source\": \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \ \"order\": null,\n \"dispute\": null\n },\n \"updated_at\": - \"2014-07-01T15:44:48.009128Z\",\n \"created_at\": \"2014-07-01T15:44:47.335350Z\",\n - \ \"transaction_number\": \"W589-042-5118\",\n \"failure_reason\": + \"2014-07-28T12:45:13.689850Z\",\n \"created_at\": \"2014-07-28T12:45:13.269306Z\",\n + \ \"transaction_number\": \"W428-667-1972\",\n \"failure_reason\": null,\n \"currency\": \"USD\",\n \"amount\": 1576,\n \"failure_reason_code\": - null,\n \"meta\": {},\n \"href\": \"/debits/WD4UhNrdn6rW5mo9B8dy3Vad\",\n - \ \"appears_on_statement_as\": \"BAL*example.com\",\n \"id\": \"WD4UhNrdn6rW5mo9B8dy3Vad\"\n - \ }\n ],\n \"links\": {\n \"debits.customer\": \"/customers/{debits.customer}\",\n + null,\n \"meta\": {\n \"state\": \"new\",\n \"participant_id\": + \"2\"\n },\n \"href\": \"/debits/WD8C07Yk8Sw3QuUqCbilLgh\",\n \"appears_on_statement_as\": + \"BAL*example.com\",\n \"id\": \"WD8C07Yk8Sw3QuUqCbilLgh\"\n }\n ],\n + \ \"links\": {\n \"debits.customer\": \"/customers/{debits.customer}\",\n \ \"debits.dispute\": \"/disputes/{debits.dispute}\",\n \"debits.source\": \"/resources/{debits.source}\",\n \"debits.order\": \"/orders/{debits.order}\",\n \ \"debits.refunds\": \"/debits/{debits.id}/refunds\",\n \"debits.events\": \"/debits/{debits.id}/events\"\n }\n}"} headers: - content-length: ['1002'] + content-length: ['1061'] content-type: [application/json] status: {code: 201, message: CREATED} +- request: + body: '{"status": "succeeded", "description": "janet", "amount": 1576, "created_at": + "2014-07-28T12:45:12.631439Z", "card_href": "/cards/CC3MHBsW6vHaL3suLVdTVbO9", + "updated_at": "2014-07-28T12:45:12.850022Z", "expires_at": "2014-08-04T12:45:12.764652Z", + "failure_reason": null, "currency": "USD", "transaction_number": "HL977-592-8484", + "meta": {"state": "captured", "participant_id": "2"}, "debit": null, "failure_reason_code": + null, "voided_at": null, "id": "HL7TDxM5fKRKe3RqQYc6eNv"}' + headers: {} + method: PUT + uri: https://api.balancedpayments.com:443/card_holds/HL7TDxM5fKRKe3RqQYc6eNv + response: + body: {string: !!python/unicode "{\n \"card_holds\": [\n {\n \"status\": + \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"card\": + \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \"debit\": \"WD8C07Yk8Sw3QuUqCbilLgh\"\n + \ },\n \"updated_at\": \"2014-07-28T12:45:14.126939Z\",\n \"created_at\": + \"2014-07-28T12:45:12.631439Z\",\n \"transaction_number\": \"HL977-592-8484\",\n + \ \"expires_at\": \"2014-08-04T12:45:12.764652Z\",\n \"failure_reason\": + null,\n \"currency\": \"USD\",\n \"amount\": 1576,\n \"meta\": + {\n \"state\": \"captured\",\n \"participant_id\": \"2\"\n },\n + \ \"href\": \"/card_holds/HL7TDxM5fKRKe3RqQYc6eNv\",\n \"failure_reason_code\": + null,\n \"voided_at\": null,\n \"id\": \"HL7TDxM5fKRKe3RqQYc6eNv\"\n + \ }\n ],\n \"links\": {\n \"card_holds.events\": \"/card_holds/{card_holds.id}/events\",\n + \ \"card_holds.card\": \"/cards/{card_holds.card}\",\n \"card_holds.debits\": + \"/card_holds/{card_holds.id}/debits\",\n \"card_holds.debit\": \"/debits/{card_holds.debit}\"\n + \ }\n}"} + headers: + content-length: ['968'] + content-type: [application/json] + status: {code: 200, message: OK} - request: body: null headers: {} @@ -127,18 +308,18 @@ interactions: body: {string: !!python/unicode "{\n \"credits\": [\n {\n \"status\": \"succeeded\",\n \"description\": \"homer\",\n \"links\": {\n \"customer\": \"CU3O2IKYocPZ5LA7GkZWpwKZ\",\n \"destination\": \"BA3QLnsEGgrsjqfhXh43Pq4d\",\n - \ \"order\": null\n },\n \"updated_at\": \"2014-07-01T15:44:49.736053Z\",\n - \ \"created_at\": \"2014-07-01T15:44:49.468347Z\",\n \"transaction_number\": - \"CR096-343-6203\",\n \"failure_reason\": null,\n \"currency\": + \ \"order\": null\n },\n \"updated_at\": \"2014-07-28T12:45:15.420772Z\",\n + \ \"created_at\": \"2014-07-28T12:45:15.147750Z\",\n \"transaction_number\": + \"CR799-163-2280\",\n \"failure_reason\": null,\n \"currency\": \"USD\",\n \"amount\": 1500,\n \"failure_reason_code\": null,\n - \ \"meta\": {},\n \"href\": \"/credits/CR4WGfyhoAK9GlZY845TaPch\",\n - \ \"appears_on_statement_as\": \"example.com\",\n \"id\": \"CR4WGfyhoAK9GlZY845TaPch\"\n + \ \"meta\": {},\n \"href\": \"/credits/CRaID8fpEYnsVwGSlUuCAZW\",\n + \ \"appears_on_statement_as\": \"example.com\",\n \"id\": \"CRaID8fpEYnsVwGSlUuCAZW\"\n \ }\n ],\n \"links\": {\n \"credits.order\": \"/orders/{credits.order}\",\n \ \"credits.customer\": \"/customers/{credits.customer}\",\n \"credits.destination\": \"/resources/{credits.destination}\",\n \"credits.reversals\": \"/credits/{credits.id}/reversals\",\n \ \"credits.events\": \"/credits/{credits.id}/events\"\n }\n}"} headers: - content-length: ['955'] + content-length: ['953'] content-type: [application/json] status: {code: 201, message: CREATED} - request: @@ -189,98 +370,69 @@ interactions: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU3O2IKYocPZ5LA7GkZWpwKZ/credits?limit=10&offset=0 - response: - body: - string: !!binary | - H4sIAAAAAAAAA61UXW/TMBR936+o/MATXdLGbddKEyoDVaiTKGPTWBGKjHNDzBI7s52xqcp/x3bj - JCuqeICXKLkf55z7ld3JYICohIRphRaDr+ZzMNi5p3EoTXRl7UhVlAIkkKDX3pmAopKVmgluIzJR - gOy8OeP3NtNjWZpKaRdkoi9uoo/jD+s7QTfbyeVytrrf3pa/1tsWwMQbAs048QRvl9GnS67er35I - 9fMhzb5kONo84E6RSREycfi8yvNGZ90KrsqEaEhioq2CcTjCw3A2DEfXo8kC4wWen86iaTiJOhG2 - M39JwdOzCM96KVoSrgi1qmNeFd+beq/C+XQY4Wg4HYdR16aUsLySEBse5fpolbeKaSUlcPps9d58 - ftelkUJU3JYxmoRhG/4SLKYigUPEAjSxU+m6kklILX7QbEFwcYVvV+lzJpbr+Srf3p3hyTXZ0KzH - XpZApIpNhXZDoACuY+L2BJ5IUeZwSkXRxbPEuo7iukHV5vnNVoK8RGdGOVFuXIHfHhUc2x1fwZuc - FUyfj8JXIk0V6POwkYI4PFmwrseoq75Zzn+Bd7x2KM1I0J7fWLyhlPDIhDupnggtNMlt3n6SKGXy - fxRtGujGjF7eoj/3U38sKHBvKtg1K7D31L5r3mq0myiSu0G369ImsaQOupCmFJ/bv/3eKNtk7/+D - 1HBy92s6wtj4D+gOfh1GlxKVpNCrsRdSI9uqk/o3GBlRDw0FAAA= - headers: - content-encoding: [gzip] - content-length: ['551'] - content-type: [application/json] - status: {code: 200, message: OK} -- request: - body: null - headers: {} - method: GET - uri: https://api.balancedpayments.com:443/customers/CU3O2IKYocPZ5LA7GkZWpwKZ/credits?limit=10&offset=0 - response: - body: - string: !!binary | - H4sIAAAAAAAAA61UXW/TMBR936+o/MATXdLGbddKEyoDVaiTKGPTWBGKjHNDzBI7s52xqcp/x3bj - JCuqeICXKLkf55z7ld3JYICohIRphRaDr+ZzMNi5p3EoTXRl7UhVlAIkkKDX3pmAopKVmgluIzJR - gOy8OeP3NtNjWZpKaRdkoi9uoo/jD+s7QTfbyeVytrrf3pa/1tsWwMQbAs048QRvl9GnS67er35I - 9fMhzb5kONo84E6RSREycfi8yvNGZ90KrsqEaEhioq2CcTjCw3A2DEfXo8kC4wWen86iaTiJOhG2 - M39JwdOzCM96KVoSrgi1qmNeFd+beq/C+XQY4Wg4HYdR16aUsLySEBse5fpolbeKaSUlcPps9d58 - ftelkUJU3JYxmoRhG/4SLKYigUPEAjSxU+m6kklILX7QbEFwcYVvV+lzJpbr+Srf3p3hyTXZ0KzH - XpZApIpNhXZDoACuY+L2BJ5IUeZwSkXRxbPEuo7iukHV5vnNVoK8RGdGOVFuXIHfHhUc2x1fwZuc - FUyfj8JXIk0V6POwkYI4PFmwrseoq75Zzn+Bd7x2KM1I0J7fWLyhlPDIhDupnggtNMlt3n6SKGXy - fxRtGujGjF7eoj/3U38sKHBvKtg1K7D31L5r3mq0myiSu0G369ImsaQOupCmFJ/bv/3eKNtk7/+D - 1HBy92s6wtj4D+gOfh1GlxKVpNCrsRdSI9uqk/o3GBlRDw0FAAA= - headers: - content-encoding: [gzip] - content-length: ['551'] - content-type: [application/json] - status: {code: 200, message: OK} -- request: - body: null - headers: {} - method: GET - uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd/debits?limit=10&offset=0 + uri: https://api.balancedpayments.com:443/customers/CU3O2IKYocPZ5LA7GkZWpwKZ/credits?sort=created_at%2Cdesc&limit=10&offset=0 response: body: string: !!binary | - H4sIAAAAAAAAA+1VyW7bMBC95ysCH3ooKpkSRVkKEBReChiooxTZ3AWFwYh0o0RbScqIG/jfS1GU - JQtxm6I+5mKIM5w3M28ex09Hx8e9hArcOzl+kt/yFGMu5KnXDwsusoQy3h9fw4+fyXoVXE7oevYJ - 5sM58+9+kj6ht5Hg7+MoicSpBd5kyyWn4hT03lVYKX0ssdIijrXljtHlodBVWglmAQ1epZeW2pAz - uoqygu/WIDKBY2mCOmwZsQO0LDvelIC9OEofyoyaz4ojs2az0/xTx72pqdN2EvG8EFRF6W/er4O0 - oRvDs4KFVQij1aGJqc7dkIwRylQS9dVcV8fubTnDIiVlj7IoJYFtSRHZ9Gu3pld3Qlc0FXtjtHdL - YhUkM3xTSqq4lORygYWaZ48XYUgpoUQzJp2E8pBFuYiytLxxj1MqGu/uXJTW20PZJ/ItQJl9y+x4 - DM+mIz53V1M8g7yY3ZCrm9tzv327JrWRv0rajLR0qPa0dJS7yAkWlCyweoU2sBwDDAxgXVnoxHFO - HM8EwLds72vTWcjoH0MGJoQIItAKEQynHIclVYu0SG6r6c+R5xvAsQ1kWV6Dv8RRXDC6kGm4orbd - kSSRMZqG67Lc68tJE4aTrEjVA0UDtxKD7G8XbBFmpJTqDmK9kNR7UqRs14aW23ziXN8FjKQum6Mk - 80ceWcMb3NICznOKGV/I/krN0ESqb4GVckbD2Vv6iJM8pmaYJU3BESnde7FVMbqmV0FuBYlM5HjI - tf5BkI7p2y602hreI0jbBQZ0PMNCNjygIAHQfxEHFOTF0FmHV+Gv/IKcAQqDafAlQq3X8D+CfB77 - VZDPbUgITRtBC9gvFyS0TeQBCPy/b0jXAcZgAAzHc5pl31lq3X32gg0JXOvgG3Iy9YPx/Qc2igIE - HmP7Bxg+BAcS5PPYlSDl7/ejzW/H5EyAWwoAAA== + H4sIAAAAAAAAA+WZW2/iOBSA3+dXVJF2njbBd8eRqlVLKXTohYG2FFYrlCZOyRYSSEKZtup/XycQ + yHSbHbTMSEi8RBD7XOx88vE55/XTwYHmRNL1k1izDv5Ufw8OXrOnGogTO5ml77V45jhSutLVfs8H + XRk7kT9J/DBIZwzDsYzWoyM/eEwlc12pmVmcZJPU7OoNvkJnzV7otPr0/IjXH/vdybzZXylQ85WB + xA/s3MDxEf56HsS1+kMU/z31hndDgltTsvZIiYSRm+kPZqPR0s+3lcOziWsn0h3YSeoBApDogOvI + vIbIItSC1CAIcI7WTqQ78wMRSDinoCCSRHYQ207q9SCYje+X621zIXTIsI6QCdbb5Nn+aBbJgbIT + Z/uYer7y2JlFkQyc59Tfm87JWsweh7MgXQakAKymf69s4ISufK9xLBM7/SrrXRlG0kv1V5YUVKpt + ++zE9Ca1XhDfzuud0c2setTvFoxPJtKO4oFaYAqIHMsgGdgZJvKbPZ6MpOGE4/V8302HytRmn2np + zl6BB9k15BYQFoAGUxBR8UPwiiKIUwaLrJaCB6BuMqGbCIgdB491zIt+f1r3zvBj0v5yNbk8HYrG + 9uSV6N139LCFsMEhgmBj9DIRQkwg6AZnnsDqiMVc51jAHUevef/gfG2Pw1h2+o8nwW3PZxeFFdr/ + 88z7WO1eg8cswi0IDGxiyMhGZ14uooKtOvY2AI8IokMBdcEJ2XHwWFyvD91L4VXd8+dnWW+wfu8Y + /YQz72O9e44eIhbiBmNIYL4hegsRgikGRZGycAuBqTNKdKyM7Dh6lDt3Z/7LvOE53eNaIwzk9NKM + t0evRO+eowdNCxODEchBMV/4OMVIb3rMWohgrnKHYoQuQ48TlV4IqlOM+Y6jx57stimSKr/5EnSj + DoXzzi2+3h69Er17jR5V3FlIoYegCYvRsxy9XAQziMEmAVekSYbKpgVhu55k0HtYC+67DVmberLX + 8P3a6P7E2x69Er17jR6xCLIQMzAyVTjcKODmIoBxINgGdz1uEl1QqBKNnb/rYfEwPGNTeUlIYsOr + aD7kL6et7dEr0bvX6CGLIgsyA0GsIu5G6C1EqKEOMcQ2CbgCQp0jqhMA6K4H3HrY675A0mwn3sSE + 5HRO5sfbk8c+VLvn4BFmUWoAZkJzsyRD1Z+VCDFMpGgqpsRlNz1AVG5LhCqsmLteWEHk5ap1Mele + 3uETYHevWqQpWv725JXoXaCnnn+lZXEtr3dnr7WRHWe1/0reiogrZY2IvBz+RxxGyeH6pvQbqqaN + j88jf+wnhxB8Dj0vlskhy4v6WiC//SojcGVkXbVfNlV+3kpWNrIlpj2GZYdBWyxVvclfTCL55IdZ + h2jdt9CSMLFH6hVDi86E5vnRL9t3oKlPm7UPtO9bTnlXy5BPqj+RdSdWLY7XZa/D8N23ynJ86Ws+ + kneStEr2K66sZLL/b0uAV1bUTqhZ9ug/DK2nvLP1rtFViWQcziJHFowWpvzLdLGvViB75XA+/pZt + 1ae3fwBwhxeM9BsAAA== headers: content-encoding: [gzip] - content-length: ['712'] + content-length: ['1153'] content-type: [application/json] status: {code: 200, message: OK} - request: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd/debits?limit=10&offset=0 + uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd/debits?sort=created_at%2Cdesc&limit=10&offset=0 response: body: string: !!binary | - H4sIAAAAAAAAA+1VyW7bMBC95ysCH3ooKpkSRVkKEBReChiooxTZ3AWFwYh0o0RbScqIG/jfS1GU - JQtxm6I+5mKIM5w3M28ex09Hx8e9hArcOzl+kt/yFGMu5KnXDwsusoQy3h9fw4+fyXoVXE7oevYJ - 5sM58+9+kj6ht5Hg7+MoicSpBd5kyyWn4hT03lVYKX0ssdIijrXljtHlodBVWglmAQ1epZeW2pAz - uoqygu/WIDKBY2mCOmwZsQO0LDvelIC9OEofyoyaz4ojs2az0/xTx72pqdN2EvG8EFRF6W/er4O0 - oRvDs4KFVQij1aGJqc7dkIwRylQS9dVcV8fubTnDIiVlj7IoJYFtSRHZ9Gu3pld3Qlc0FXtjtHdL - YhUkM3xTSqq4lORygYWaZ48XYUgpoUQzJp2E8pBFuYiytLxxj1MqGu/uXJTW20PZJ/ItQJl9y+x4 - DM+mIz53V1M8g7yY3ZCrm9tzv327JrWRv0rajLR0qPa0dJS7yAkWlCyweoU2sBwDDAxgXVnoxHFO - HM8EwLds72vTWcjoH0MGJoQIItAKEQynHIclVYu0SG6r6c+R5xvAsQ1kWV6Dv8RRXDC6kGm4orbd - kSSRMZqG67Lc68tJE4aTrEjVA0UDtxKD7G8XbBFmpJTqDmK9kNR7UqRs14aW23ziXN8FjKQum6Mk - 80ceWcMb3NICznOKGV/I/krN0ESqb4GVckbD2Vv6iJM8pmaYJU3BESnde7FVMbqmV0FuBYlM5HjI - tf5BkI7p2y602hreI0jbBQZ0PMNCNjygIAHQfxEHFOTF0FmHV+Gv/IKcAQqDafAlQq3X8D+CfB77 - VZDPbUgITRtBC9gvFyS0TeQBCPy/b0jXAcZgAAzHc5pl31lq3X32gg0JXOvgG3Iy9YPx/Qc2igIE - HmP7Bxg+BAcS5PPYlSDl7/ejzW/H5EyAWwoAAA== + H4sIAAAAAAAAA+2Z62/iRhDAv99fESH1PlS1sy/vrpGiKhBS2lByF15Nqgo59kKcgO34waNR/veu + H2AgcMf1HClt+BLZu56ZnZnfTmaXpw9HR6WxCI1S+ehJPsu3kRGE8q10bEZB6I6FHxxXO/jiD2s+ + abbOxLzxCXunPV+/e7SOLXFrh8HPgeuHJ6YvjFBYfSP8AVUtEZgfR/bYDk8g+OgOBoEITxAEpZ9S + I46YvZKR3MadLwav48jSjcRDaQOCzK/UUzmyGPB8MbHdKJBDTjQaZZ+FbmiM5BCCMBsZ2P5rhR2U + ZMyfYzulke08xEvJUp2mT10keiNYTxvTz4vkZeOWHXhRKBKp7Dk4XghlA5sygRv5Zirii/Qll0nf + N0Vc3xJ+YiR5yj9PXje/ljmPHCv2US4qoXO5JNt6Pl5MZ1HPPBET4YQ7ZbLZZRBTIWnhz4TlNJYy + uEFohEmiS0FkmkJYwsoiJifjDeHbXmi7TvzFveGIMJ9dz0uyDVeTsmv/LRXE1peRrVbx7/VK0KOT + utHAQdToWu3u7aW++vUiqDmTidE8pfFE4l6GTjIdeVa2xWMfEIBEAUxBvA1RmWhliFXKda6Bm9yz + vCrsEkFUx4CuiIS+4QSGGYeq70Tj2zT7PYK4QilToM5Qrn9g2KPIF31ZfIIktKseySD6vnDMeWy7 + 0zrLxYyxGznJztUYTWGQ/q0r65uuFaO6pnGtViZBidOeEO2I6WqIPcMPbdP2DCfs21bifbwR1yO6 + rFEZq70zXgXs+oG3pvhz1Hms3tqjxvBuZeGeJww/6MvYJIbHkty+kVBXOW38KGbG2BsJ1XTHuUxq + fZfqZE1JeTiwvMIyVAFDmGvfwDJQKSY63INlQLgCdKRQDRfJMgDZv503wrJWs2dujQzBNdDGRhtc + dJzKfTEsb1d9YPllXSZlwFXOOaRoX5YTEcIY0vDX6zImVOGcKAwC/j+uy/Si2e5+Nm8nQ2M0qAl8 + ecaqV5NiYN6h+0DzVpqpCrGO1lqGL3YZkmZNpYwyvE9lhlihHCkE8wIrMwJ61ui/kcpM67XK5XW3 + hj0Ia7ptuV4wDVbaVeM72owdug80b6UZq5gRQPRvqM1I1QmCFH69NhMOFVn4FZ1RrcDa/Nb6DFo7 + tVsP9YY/fTD4Q7My/JuAeVE0b9d9oPkFzZiWCVKZBiFbLbRfqs2pCCQA0X06DUAVSJgCuAaLpJkW + VZtNwwvlYTQnT1Z7MTPvDGcosjNgvnA59y+PiNS/CuH5RHeazWG33rRvQ7ty1SqoFdmu+4D7S9xJ + mcguGXAE2Z7FGyciAEAIVkV2XXhATZFNu4J1/q5xJ05knfe08QOpeae1zo0N5+ddUQzuO3QfcH+B + O8Tx/R6BgMB970RSEQAIYKsiO3DHmjxHyjsRjkmR50jwX6vuZGi0Z4BefBrXamI2hPNmfbByeVv6 + ntZ8h+4D7i9xR2WoqTrkZO/WXF6CSxFNwq7v05pD2ZprTMFcZ++5mcHWZOL82q53p5zbo1mr/UvP + vcHFVPcdug+4b+AOy3EjDlT5A61G9vv1JhHBuso4Q2QP3OUhV9ExVJD8yfNd4/74W5UxisLG/elU + Z1fTs87N8Log3LfrTnGXf//68PwP4sdp3vAfAAA= headers: content-encoding: [gzip] - content-length: ['712'] + content-length: ['1226'] content-type: [application/json] status: {code: 200, message: OK} version: 1 diff --git a/tests/py/fixtures/TestPayin.yml b/tests/py/fixtures/TestPayin.yml new file mode 100644 index 0000000000..45b584e25f --- /dev/null +++ b/tests/py/fixtures/TestPayin.yml @@ -0,0 +1,374 @@ +interactions: +- request: + body: null + headers: {} + method: GET + uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd + response: + body: + string: !!binary | + H4sIAAAAAAAAA41Uy27bMBC85ysEnWvLipQ29i1oby2CAG3RokUh0CQLEZFIhY+ggqF/79KWZJpU + wlx02J0ZcmdHPFwlSYqN0qKlUqW75DcUkuRw/EKLo5ZClZumeTfVGsYfLXQCAUwJI7EPhDqhSjOO + NBN8VBlFhlnNdARpSiqkAZFeb/Jytfmw2uTf8ptdWezKcp0Xxfvb7a90pmBJX6UU622+uSlKh0LE + vmoF17U/DCP22I/fi88/Sf98//UT7b88FN3dD7mtn8j5zK4W3B8wrSX9a+nZ7GAWVwKjcY24rpRG + 2lgjUy5WLdK4Ph/XUo08i+mTheZp4KCdradI+qMhQiRV3qYw070HhD3BSun1cjkPy/bivhcg0glo + NBUWZKGJheFa9m43GGRvFNxDqWopdUrxqkFKl/6YtEWsCYrsInADnPXH5sdO6oT3HP31HOE0A9eO + eVbZYV7s2B/GFTlEjCSpatGQ4yqdKDhkRobMwZ2S7GjsEX+sED6aFJG5hAZKhO5ZTGLELHDdv/UF + H5xfesEM+k9TySEFbxsmhAeXsrZFLDlBAiZhqjOaRsgzKuBribhC2D5eEY0LZKADj4ThsRkm0AL7 + GV5m1ESuIOkECxSEJKe3/ZVwjpiAC08tieZpAsH/NVwN/wHQiK4AUwYAAA== + headers: + content-encoding: [gzip] + content-length: ['499'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: null + headers: {} + method: GET + uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd/cards?limit=10&offset=0 + response: + body: + string: !!binary | + H4sIAAAAAAAAA6VVW2+bMBR+76+weNhTmwBOyEWqprSbVq1NWq1dO3WakIPtxiqxmW2yoCr/fbaB + AL1o08YDEud+vvOdw9MBAF6CJFbeFHw3HwA8ubcRp4w/WnEtsJa50mJNpJF6p1/h+TdcbBbXH0hx + cQWz2Z2crH5ir/LfHdaBKOMPRGaScW0dx8mI+oMQwmiI6HicQAKjhPrhaBCRBEUDihMKI4yTkAQU + Tow5SSYUUor9IQqjkbcPnGw2sSQqT21cnqfpXsPz9bIsc9t6AvM03mij4kwojdJ4jXSysrVx0ejJ + NmMSaSZ4vBZcW33g7zOsiUZdcCR5MLYOGsFpruxHbW6xY7qItfjlLNoKhLFpQsWhVZyILQhfQsiw + i3sK52cn6i7anKELqPKLW3xzu7yctBBBmjwIWVhroVcGgn3BusiIFSeSYKa7INb9dzBcIv4Yc7R2 + Xp+v5pdfPs0W4PRsdv0RnMwW54dg0Zv1ungqLQnRDZ4FUY3BUiLu+rhlCjXiPMOmahwjx47QDwZH + /ujID26C4XQAp4NhbzwMQhjct1KVkBn7NjkNwEbS7qFkMbHIviIObD4TGcwR4+Dald4ejKGGds2/ + Os+KOonAzmbih4Hf9k5EzrUs4srA5n+xGQniMSZLM44p0DInDX9L2DvzaPGxIMjuYOiHDSHNMjxr + 02Mq3hDJKCMW9k4CS/796nhl76BiIjBjAmV7wFYPsABcaOBY0hq4rb5i0xRQlKqm/pUk1KLSd7el + /xfEleQPJBhEcBLA+3I3dgbKHxYtr7OHXoqUY1G/vlQm9xt3qiztfcrWTB8H/jtBqSL6uJ6hx8m2 + e1a8pqnqDP57cJfVFFofFK/MbiTVQL1Mkg0TuT3ADQs8Lcy5sn4lUzzK5P83bGB0x/rZxS//Cz3M + VJZrs8fNOJ/cVHsM7/p7bVVQqWn/J1qjqPxq7a5alyqRdY1XInX/opo5rVQtfSeZW6A3fCqd7fBg + 9xv4QHhu7QYAAA== + headers: + content-encoding: [gzip] + content-length: ['751'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: '{"amount": 1061, "meta": {"state": "new", "participant_id": 2}, "description": + "janet"}' + headers: {} + method: POST + uri: https://api.balancedpayments.com:443/cards/CC3MHBsW6vHaL3suLVdTVbO9/card_holds + response: + body: {string: !!python/unicode "{\n \"card_holds\": [\n {\n \"status\": + \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"card\": + \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \"debit\": null\n },\n \"updated_at\": + \"2014-07-28T13:06:03.447969Z\",\n \"created_at\": \"2014-07-28T13:06:02.929188Z\",\n + \ \"transaction_number\": \"HL204-775-3704\",\n \"expires_at\": \"2014-08-04T13:06:03.363939Z\",\n + \ \"failure_reason\": null,\n \"currency\": \"USD\",\n \"amount\": + 1061,\n \"meta\": {\n \"state\": \"new\",\n \"participant_id\": + \"2\"\n },\n \"href\": \"/card_holds/HL7e0PQfQDeqDvm9LpSVYwEV\",\n + \ \"failure_reason_code\": null,\n \"voided_at\": null,\n \"id\": + \"HL7e0PQfQDeqDvm9LpSVYwEV\"\n }\n ],\n \"links\": {\n \"card_holds.events\": + \"/card_holds/{card_holds.id}/events\",\n \"card_holds.card\": \"/cards/{card_holds.card}\",\n + \ \"card_holds.debits\": \"/card_holds/{card_holds.id}/debits\",\n \"card_holds.debit\": + \"/debits/{card_holds.debit}\"\n }\n}"} + headers: + content-length: ['944'] + content-type: [application/json] + status: {code: 201, message: CREATED} +- request: + body: '{"status": "succeeded", "transaction_number": "HL204-775-3704", "description": + "janet", "amount": 1061, "created_at": "2014-07-28T13:06:02.929188Z", "card_href": + "/cards/CC3MHBsW6vHaL3suLVdTVbO9", "updated_at": "2014-07-28T13:06:03.447969Z", + "expires_at": "2014-08-04T13:06:03.363939Z", "failure_reason": null, "currency": + "USD", "is_void": true, "meta": {"state": "cancelled", "participant_id": "2"}, + "debit": null, "failure_reason_code": null, "voided_at": null, "id": "HL7e0PQfQDeqDvm9LpSVYwEV"}' + headers: {} + method: PUT + uri: https://api.balancedpayments.com:443/card_holds/HL7e0PQfQDeqDvm9LpSVYwEV + response: + body: {string: !!python/unicode "{\n \"card_holds\": [\n {\n \"status\": + \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"card\": + \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \"debit\": null\n },\n \"updated_at\": + \"2014-07-28T13:06:03.805059Z\",\n \"created_at\": \"2014-07-28T13:06:02.929188Z\",\n + \ \"transaction_number\": \"HL204-775-3704\",\n \"expires_at\": \"2014-08-04T13:06:03.363939Z\",\n + \ \"failure_reason\": null,\n \"currency\": \"USD\",\n \"amount\": + 1061,\n \"meta\": {\n \"state\": \"cancelled\",\n \"participant_id\": + \"2\"\n },\n \"href\": \"/card_holds/HL7e0PQfQDeqDvm9LpSVYwEV\",\n + \ \"failure_reason_code\": null,\n \"voided_at\": \"2014-07-28T13:06:03.805061Z\",\n + \ \"id\": \"HL7e0PQfQDeqDvm9LpSVYwEV\"\n }\n ],\n \"links\": {\n + \ \"card_holds.events\": \"/card_holds/{card_holds.id}/events\",\n \"card_holds.card\": + \"/cards/{card_holds.card}\",\n \"card_holds.debits\": \"/card_holds/{card_holds.id}/debits\",\n + \ \"card_holds.debit\": \"/debits/{card_holds.debit}\"\n }\n}"} + headers: + content-length: ['975'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: null + headers: {} + method: GET + uri: https://api.balancedpayments.com:443/card_holds?meta.state=new&limit=25&offset=0 + response: + body: + string: !!binary | + H4sIAAAAAAAAA92ZWU/bShTH3/spkB/6dG1mXyyhK7Y2gUBZ0lC4uooce0JcgpN4yQLiu3fsbI4a + C6uNkJKXCM2c4/+Z4aczM+e8ftrbM1wn9JqdXteLDHvvPz2yt/ea/eq5KHbiJB03osR1lfKUZ/wz + n/RU5IZ+P/Z7QWrx0wlUvJzt+sFT6jn/1kwptTw+xheVo+iODStODUdJreHVG61vcuGsbT3V8mNt + HCTd7kzwbaGc9D0nVl7TSS0MBCAxATeRqENsA2ojaXFKJZMPy3DcUL3jQrGEgORc4tAJIsdN19cM + kueWClO1So0TZhIMTSoJWQqocd8PVbQSkzABWcbEmARM5ATajt9NQtXUoUXZHqaLXSzSTcJQBe4k + Ff1+e7JUcp57SZCuHAEJF+bPKnZWtzv956nUO1Cj/Nb2nTD2Xb/vBHHT97IdNH7b4k6o2unU/hKP + /UqNHUdo+PQwcF7ugtP4hZ7e3l/GP5ahra6o6fa8NICVZQ17vqZouk0rE9NQCjWyCGcIbC+fyMbQ + xtjCjADOS/E5d4EEYUpL8Cmo5pNqPgllZficCyABgMzHtH18Xj91ru6/JBWVsOGZHJxWnujXi83i + uV5iN+gENhU2BRaFAmNUis65C9apjZWhk2Nq6kRrYp1uy9A5FyAYY57P6H9LJyZSfmz2hN5oNCLu + QIUt1/U61dbl2dFpY7N4FmjsEJ8QWpIKLMplzwwf7cIRELAMn0JCE1BkIg5BaT61gOBASrzB0/3j + +RwJeXdzXz0fdB4TR9LO/QiMJ5vFc73ErtAJhE2oxRlAK5mq+O4J7KkLZVhiUOJspzp7IqHvuoiU + pHMqwDjlFG41nbAdf629KIgdMBo7PwZXF+f8e+GK/uzuWaCxM3wiG3ILaTxJ2ewJtAuzJBdIsBJ8 + MgFMQqTJKRblsmcqwC2IkBDbnT3hQzIcEFE7m0wak5uTyVPMwqi62fRZoLH9fEJWh9wGLGWBIK4P + 4Xdvn3kXpC+HJI9P0dsdM2giJkwO2bLcYKx7u+sci5cx6a9Lni8O/O3t8+Pf7rh++jPut5LOZf36 + 8nF8fHVWTS5yBZtNvN0LNHaGT2ITbglE9GOkLJ+ZC9O4kTLnO6DUhFK/3zl/p7a04DMT4FjfbzeZ + Pz+eT/R4w65PTuS42lDfJKq3nVoSXG02fxZo7Ayf2Ma69qmLk+j92ucsf2YuFOlqab40WZQ/KSAm + xcjUObps/swEmC4pyO3OnxB1XPewen/TQucvI/e2dfTMk6PN8lmgsRt8MptqFvRLWVJG8iysfx+l + fM5dmICrz5ciPjkSJsRC1z9Bqfw5F+CSCJm/4G7f+U4Pz/0+aYzPv8Dx1c3hWej1EvyyWT4LNKZ8 + 6t//04KasdKKMLpOlLVpcp2Ef7v+sx8fIPo5NbWyRsWB7lJ87rXbkYoP5k9b3bkYz5tA01KdsaYv + UfprmWraN6Gzj03l9AiYDfRDNfR7Wcdr2Zsw4l7sdPUQnJu1/fAP1qS3JutfGKutsVwDzsq6Xlm/ + Ld93eV32YCzfe9ufWc1izk2mfy6co/28Xzr1NoPhN8XMZ/rVFadsaJ2XGqrg/ThnVum6P739AhEQ + I7VsHAAA + headers: + content-encoding: [gzip] + content-length: ['1203'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: '{"status": "succeeded", "transaction_number": "HL728-138-4504", "description": + "janet", "amount": 2091, "created_at": "2014-07-16T16:53:31.681751Z", "card_href": + "/cards/CC3MHBsW6vHaL3suLVdTVbO9", "updated_at": "2014-07-16T16:53:31.895644Z", + "expires_at": "2014-07-23T16:53:31.794896Z", "failure_reason": null, "currency": + "USD", "is_void": true, "meta": {"state": "cancelled", "participant_id": "2"}, + "debit": null, "failure_reason_code": null, "voided_at": null, "id": "HL5AKip4VxKF1xPRAJrdou3z"}' + headers: {} + method: PUT + uri: https://api.balancedpayments.com:443/card_holds/HL5AKip4VxKF1xPRAJrdou3z + response: + body: {string: !!python/unicode "{\n \"card_holds\": [\n {\n \"status\": + \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"card\": + \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \"debit\": null\n },\n \"updated_at\": + \"2014-07-28T13:06:04.874888Z\",\n \"created_at\": \"2014-07-16T16:53:31.681751Z\",\n + \ \"transaction_number\": \"HL728-138-4504\",\n \"expires_at\": \"2014-07-23T16:53:31.794896Z\",\n + \ \"failure_reason\": null,\n \"currency\": \"USD\",\n \"amount\": + 2091,\n \"meta\": {\n \"state\": \"cancelled\",\n \"participant_id\": + \"2\"\n },\n \"href\": \"/card_holds/HL5AKip4VxKF1xPRAJrdou3z\",\n + \ \"failure_reason_code\": null,\n \"voided_at\": \"2014-07-28T13:06:04.874891Z\",\n + \ \"id\": \"HL5AKip4VxKF1xPRAJrdou3z\"\n }\n ],\n \"links\": {\n + \ \"card_holds.events\": \"/card_holds/{card_holds.id}/events\",\n \"card_holds.card\": + \"/cards/{card_holds.card}\",\n \"card_holds.debits\": \"/card_holds/{card_holds.id}/debits\",\n + \ \"card_holds.debit\": \"/debits/{card_holds.debit}\"\n }\n}"} + headers: + content-length: ['975'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: null + headers: {} + method: GET + uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd + response: + body: + string: !!binary | + H4sIAAAAAAAAA41Uy27bMBC85ysEnWvLipQ29i1oby2CAG3RokUh0CQLEZFIhY+ggqF/79KWZJpU + wlx02J0ZcmdHPFwlSYqN0qKlUqW75DcUkuRw/EKLo5ZClZumeTfVGsYfLXQCAUwJI7EPhDqhSjOO + NBN8VBlFhlnNdARpSiqkAZFeb/Jytfmw2uTf8ptdWezKcp0Xxfvb7a90pmBJX6UU622+uSlKh0LE + vmoF17U/DCP22I/fi88/Sf98//UT7b88FN3dD7mtn8j5zK4W3B8wrSX9a+nZ7GAWVwKjcY24rpRG + 2lgjUy5WLdK4Ph/XUo08i+mTheZp4KCdradI+qMhQiRV3qYw070HhD3BSun1cjkPy/bivhcg0glo + NBUWZKGJheFa9m43GGRvFNxDqWopdUrxqkFKl/6YtEWsCYrsInADnPXH5sdO6oT3HP31HOE0A9eO + eVbZYV7s2B/GFTlEjCSpatGQ4yqdKDhkRobMwZ2S7GjsEX+sED6aFJG5hAZKhO5ZTGLELHDdv/UF + H5xfesEM+k9TySEFbxsmhAeXsrZFLDlBAiZhqjOaRsgzKuBribhC2D5eEY0LZKADj4ThsRkm0AL7 + GV5m1ESuIOkECxSEJKe3/ZVwjpiAC08tieZpAsH/NVwN/wHQiK4AUwYAAA== + headers: + content-encoding: [gzip] + content-length: ['499'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: null + headers: {} + method: GET + uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd/cards?limit=10&offset=0 + response: + body: + string: !!binary | + H4sIAAAAAAAAA6VVW2+bMBR+76+weNhTmwBOyEWqprSbVq1NWq1dO3WakIPtxiqxmW2yoCr/fbaB + AL1o08YDEud+vvOdw9MBAF6CJFbeFHw3HwA8ubcRp4w/WnEtsJa50mJNpJF6p1/h+TdcbBbXH0hx + cQWz2Z2crH5ir/LfHdaBKOMPRGaScW0dx8mI+oMQwmiI6HicQAKjhPrhaBCRBEUDihMKI4yTkAQU + Tow5SSYUUor9IQqjkbcPnGw2sSQqT21cnqfpXsPz9bIsc9t6AvM03mij4kwojdJ4jXSysrVx0ejJ + NmMSaSZ4vBZcW33g7zOsiUZdcCR5MLYOGsFpruxHbW6xY7qItfjlLNoKhLFpQsWhVZyILQhfQsiw + i3sK52cn6i7anKELqPKLW3xzu7yctBBBmjwIWVhroVcGgn3BusiIFSeSYKa7INb9dzBcIv4Yc7R2 + Xp+v5pdfPs0W4PRsdv0RnMwW54dg0Zv1ungqLQnRDZ4FUY3BUiLu+rhlCjXiPMOmahwjx47QDwZH + /ujID26C4XQAp4NhbzwMQhjct1KVkBn7NjkNwEbS7qFkMbHIviIObD4TGcwR4+Dald4ejKGGds2/ + Os+KOonAzmbih4Hf9k5EzrUs4srA5n+xGQniMSZLM44p0DInDX9L2DvzaPGxIMjuYOiHDSHNMjxr + 02Mq3hDJKCMW9k4CS/796nhl76BiIjBjAmV7wFYPsABcaOBY0hq4rb5i0xRQlKqm/pUk1KLSd7el + /xfEleQPJBhEcBLA+3I3dgbKHxYtr7OHXoqUY1G/vlQm9xt3qiztfcrWTB8H/jtBqSL6uJ6hx8m2 + e1a8pqnqDP57cJfVFFofFK/MbiTVQL1Mkg0TuT3ADQs8Lcy5sn4lUzzK5P83bGB0x/rZxS//Cz3M + VJZrs8fNOJ/cVHsM7/p7bVVQqWn/J1qjqPxq7a5alyqRdY1XInX/opo5rVQtfSeZW6A3fCqd7fBg + 9xv4QHhu7QYAAA== + headers: + content-encoding: [gzip] + content-length: ['751'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: '{"amount": 4357, "meta": {"state": "new", "participant_id": 2}, "description": + "janet"}' + headers: {} + method: POST + uri: https://api.balancedpayments.com:443/cards/CC3MHBsW6vHaL3suLVdTVbO9/card_holds + response: + body: {string: !!python/unicode "{\n \"card_holds\": [\n {\n \"status\": + \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"card\": + \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \"debit\": null\n },\n \"updated_at\": + \"2014-07-28T13:06:06.523916Z\",\n \"created_at\": \"2014-07-28T13:06:06.298707Z\",\n + \ \"transaction_number\": \"HL055-552-8105\",\n \"expires_at\": \"2014-08-04T13:06:06.431593Z\",\n + \ \"failure_reason\": null,\n \"currency\": \"USD\",\n \"amount\": + 4357,\n \"meta\": {\n \"state\": \"new\",\n \"participant_id\": + \"2\"\n },\n \"href\": \"/card_holds/HL7hNoqSlwMFeMD4jknZ37wh\",\n + \ \"failure_reason_code\": null,\n \"voided_at\": null,\n \"id\": + \"HL7hNoqSlwMFeMD4jknZ37wh\"\n }\n ],\n \"links\": {\n \"card_holds.events\": + \"/card_holds/{card_holds.id}/events\",\n \"card_holds.card\": \"/cards/{card_holds.card}\",\n + \ \"card_holds.debits\": \"/card_holds/{card_holds.id}/debits\",\n \"card_holds.debit\": + \"/debits/{card_holds.debit}\"\n }\n}"} + headers: + content-length: ['944'] + content-type: [application/json] + status: {code: 201, message: CREATED} +- request: + body: '{"status": "succeeded", "transaction_number": "HL055-552-8105", "description": + "janet", "amount": 4357, "created_at": "2014-07-28T13:06:06.298707Z", "card_href": + "/cards/CC3MHBsW6vHaL3suLVdTVbO9", "updated_at": "2014-07-28T13:06:06.523916Z", + "expires_at": "2014-08-04T13:06:06.431593Z", "failure_reason": null, "currency": + "USD", "is_void": true, "meta": {"state": "cancelled", "participant_id": "2"}, + "debit": null, "failure_reason_code": null, "voided_at": null, "id": "HL7hNoqSlwMFeMD4jknZ37wh"}' + headers: {} + method: PUT + uri: https://api.balancedpayments.com:443/card_holds/HL7hNoqSlwMFeMD4jknZ37wh + response: + body: {string: !!python/unicode "{\n \"card_holds\": [\n {\n \"status\": + \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"card\": + \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \"debit\": null\n },\n \"updated_at\": + \"2014-07-28T13:06:06.900887Z\",\n \"created_at\": \"2014-07-28T13:06:06.298707Z\",\n + \ \"transaction_number\": \"HL055-552-8105\",\n \"expires_at\": \"2014-08-04T13:06:06.431593Z\",\n + \ \"failure_reason\": null,\n \"currency\": \"USD\",\n \"amount\": + 4357,\n \"meta\": {\n \"state\": \"cancelled\",\n \"participant_id\": + \"2\"\n },\n \"href\": \"/card_holds/HL7hNoqSlwMFeMD4jknZ37wh\",\n + \ \"failure_reason_code\": null,\n \"voided_at\": \"2014-07-28T13:06:06.900890Z\",\n + \ \"id\": \"HL7hNoqSlwMFeMD4jknZ37wh\"\n }\n ],\n \"links\": {\n + \ \"card_holds.events\": \"/card_holds/{card_holds.id}/events\",\n \"card_holds.card\": + \"/cards/{card_holds.card}\",\n \"card_holds.debits\": \"/card_holds/{card_holds.id}/debits\",\n + \ \"card_holds.debit\": \"/debits/{card_holds.debit}\"\n }\n}"} + headers: + content-length: ['975'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: null + headers: {} + method: GET + uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd + response: + body: + string: !!binary | + H4sIAAAAAAAAA41Uy27bMBC85ysEnWvLipQ29i1oby2CAG3RokUh0CQLEZFIhY+ggqF/79KWZJpU + wlx02J0ZcmdHPFwlSYqN0qKlUqW75DcUkuRw/EKLo5ZClZumeTfVGsYfLXQCAUwJI7EPhDqhSjOO + NBN8VBlFhlnNdARpSiqkAZFeb/Jytfmw2uTf8ptdWezKcp0Xxfvb7a90pmBJX6UU622+uSlKh0LE + vmoF17U/DCP22I/fi88/Sf98//UT7b88FN3dD7mtn8j5zK4W3B8wrSX9a+nZ7GAWVwKjcY24rpRG + 2lgjUy5WLdK4Ph/XUo08i+mTheZp4KCdradI+qMhQiRV3qYw070HhD3BSun1cjkPy/bivhcg0glo + NBUWZKGJheFa9m43GGRvFNxDqWopdUrxqkFKl/6YtEWsCYrsInADnPXH5sdO6oT3HP31HOE0A9eO + eVbZYV7s2B/GFTlEjCSpatGQ4yqdKDhkRobMwZ2S7GjsEX+sED6aFJG5hAZKhO5ZTGLELHDdv/UF + H5xfesEM+k9TySEFbxsmhAeXsrZFLDlBAiZhqjOaRsgzKuBribhC2D5eEY0LZKADj4ThsRkm0AL7 + GV5m1ESuIOkECxSEJKe3/ZVwjpiAC08tieZpAsH/NVwN/wHQiK4AUwYAAA== + headers: + content-encoding: [gzip] + content-length: ['499'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: null + headers: {} + method: GET + uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd/cards?limit=10&offset=0 + response: + body: + string: !!binary | + H4sIAAAAAAAAA6VVW2+bMBR+76+weNhTmwBOyEWqprSbVq1NWq1dO3WakIPtxiqxmW2yoCr/fbaB + AL1o08YDEud+vvOdw9MBAF6CJFbeFHw3HwA8ubcRp4w/WnEtsJa50mJNpJF6p1/h+TdcbBbXH0hx + cQWz2Z2crH5ir/LfHdaBKOMPRGaScW0dx8mI+oMQwmiI6HicQAKjhPrhaBCRBEUDihMKI4yTkAQU + Tow5SSYUUor9IQqjkbcPnGw2sSQqT21cnqfpXsPz9bIsc9t6AvM03mij4kwojdJ4jXSysrVx0ejJ + NmMSaSZ4vBZcW33g7zOsiUZdcCR5MLYOGsFpruxHbW6xY7qItfjlLNoKhLFpQsWhVZyILQhfQsiw + i3sK52cn6i7anKELqPKLW3xzu7yctBBBmjwIWVhroVcGgn3BusiIFSeSYKa7INb9dzBcIv4Yc7R2 + Xp+v5pdfPs0W4PRsdv0RnMwW54dg0Zv1ungqLQnRDZ4FUY3BUiLu+rhlCjXiPMOmahwjx47QDwZH + /ujID26C4XQAp4NhbzwMQhjct1KVkBn7NjkNwEbS7qFkMbHIviIObD4TGcwR4+Dald4ejKGGds2/ + Os+KOonAzmbih4Hf9k5EzrUs4srA5n+xGQniMSZLM44p0DInDX9L2DvzaPGxIMjuYOiHDSHNMjxr + 02Mq3hDJKCMW9k4CS/796nhl76BiIjBjAmV7wFYPsABcaOBY0hq4rb5i0xRQlKqm/pUk1KLSd7el + /xfEleQPJBhEcBLA+3I3dgbKHxYtr7OHXoqUY1G/vlQm9xt3qiztfcrWTB8H/jtBqSL6uJ6hx8m2 + e1a8pqnqDP57cJfVFFofFK/MbiTVQL1Mkg0TuT3ADQs8Lcy5sn4lUzzK5P83bGB0x/rZxS//Cz3M + VJZrs8fNOJ/cVHsM7/p7bVVQqWn/J1qjqPxq7a5alyqRdY1XInX/opo5rVQtfSeZW6A3fCqd7fBg + 9xv4QHhu7QYAAA== + headers: + content-encoding: [gzip] + content-length: ['751'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: '{"amount": 2091, "meta": {"state": "new", "participant_id": 2}, "description": + "janet"}' + headers: {} + method: POST + uri: https://api.balancedpayments.com:443/cards/CC3MHBsW6vHaL3suLVdTVbO9/card_holds + response: + body: {string: !!python/unicode "{\n \"card_holds\": [\n {\n \"status\": + \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"card\": + \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \"debit\": null\n },\n \"updated_at\": + \"2014-07-28T13:06:08.748356Z\",\n \"created_at\": \"2014-07-28T13:06:08.539996Z\",\n + \ \"transaction_number\": \"HL864-060-0327\",\n \"expires_at\": \"2014-08-04T13:06:08.662188Z\",\n + \ \"failure_reason\": null,\n \"currency\": \"USD\",\n \"amount\": + 2091,\n \"meta\": {\n \"state\": \"new\",\n \"participant_id\": + \"2\"\n },\n \"href\": \"/card_holds/HL7kjDtK34drZiCaw2bXT7X2\",\n + \ \"failure_reason_code\": null,\n \"voided_at\": null,\n \"id\": + \"HL7kjDtK34drZiCaw2bXT7X2\"\n }\n ],\n \"links\": {\n \"card_holds.events\": + \"/card_holds/{card_holds.id}/events\",\n \"card_holds.card\": \"/cards/{card_holds.card}\",\n + \ \"card_holds.debits\": \"/card_holds/{card_holds.id}/debits\",\n \"card_holds.debit\": + \"/debits/{card_holds.debit}\"\n }\n}"} + headers: + content-length: ['944'] + content-type: [application/json] + status: {code: 201, message: CREATED} +- request: + body: null + headers: {} + method: GET + uri: https://api.balancedpayments.com:443/card_holds?meta.state=new&limit=25&offset=0 + response: + body: + string: !!binary | + H4sIAAAAAAAAA92ZWU/jSBDH3+dTID/M09r0fVhCq+XYSSAwHJlwrFaRY3eISXASHzlAfPdpO5ej + wcKaiZCSlwh1V/W/uvmp3F31+mVvz3Cd0Gt2+j0vMuy9//TI3t5r9qvnotiJk3TciBLXVcpTnvHX + YtJTkRv6g9jvB6nFkxOoeDXb84Nu6rlYa66UWh4d4fPKYXTLRhWnhqOk1vDqjdZ3uXTWtp5q+bE2 + DpJeby74tlROBp4TK6/ppBYGApCYgJtI1CG2AbOBsDgRmLKHVThuqD5woVhKmXeJQyeIHDfdXzNI + nlsqTNUqNcG0HAMmwIivBNRk4IcqWotJmICsYmIMQSFyMbUdv5eEqqlDi7IzTDe73KSbhKEK3Gkq + +uPmeKXkPPeTIN05AhIuzZ9V7Kwfd/rPU6l3oMb5ox04Yey7/sAJ4qbvZSdo/HLEnVC106n9FR77 + lRrvPh3HZ5h44YN/5IxR667O79AqtPUdNd2+lwawtq1R39cUzY5pbWIWSqFGFuEcga3mk9pIWpxS + yWRZPjMXzScEJOdSxCcnzCQYmlQSUpLPTIAxCdh288mOIjTqPgydl9vgJH6hJzf3F/HdZvks0NgN + PpGNoY2xhRkBnJfic+ECCcKUluBTUM0n1XwSysrwuRBAAgCZj2n78udVt3N5/29SUQkbncrhSaVL + v51vFs/3JXaDTmBTYVNgUSgwRqXoXLhgndpYGTo5pqZOtCbW6bYMnQsBgjHm+Yz+p3RiIuXnft2h + Nx6PiTtUYct1vU61dXF6eNLYLJ4FGjvEJ4SWpAKLctkzw0e7cAQELMOnkNAEFJmIQ1CaTy0gOJAS + b/D2+fl8joW8vb6vng07j4kjaed+DCbTzeL5vsSu0AmETajFGUBrmar4bQT0Wyp1oQxLDEp826nO + nkjotxgiJemcCTBOOYVbTSdsx99qLwpiB4wnzt3w8vyM/yjc0e+9jQo0doZPZENuIY0nKZs9gXZh + luQCiTJvdyaASYg0OcWiXPZMBbgFERJiu7MnfEhGQyJqp9NpY3p9PO3GLIyqm02fBRrbzydkdcjT + 2pJmgSCuP8If3j7zLkhfDkken6K3O2bQREyYHLJVOcx4r7akcyxexaRXlzxfHPjT2+fn15Zw/eQp + HrSSzkX96uJxcnR5Wk3OcwXFTdSWCjR2hk9iE24JRPRjpCyfmQvTuJEy33dAqQmlfr9z/kFtacln + JsCxvt9uMn9+Pp/o8ZpdHR/LSbWhvktUbzu1JLjcbP4s0NgZPrGNde1TFyfRx7XPef7MXCjS1dJ8 + abIof1JATIqRqXN02fyZCTBdUpDbnT8h6rjuP9X76xY6exm7N63DZ54cbpbPAo0Zn/r3/7RgYay1 + IoyeE2Vtmlwn4e+e/+zHB4h+TU2trFFxoLsUX/vtdqTig8XTQXcuJosm0KwUYrzTlyi9Wqaa9k3o + fLGZnB4B84FBqEZ+P+t4rXoTRtyPnZ4egguzth/+xp700WT9C2O9NZZrwFlZ1yvrt+X7Lq+rHozl + e2/7c6t5zLnJ9M+lc7Sf90un3uYw/KKY+cxWXXPKht7zUiMVfBzn3Crd95e3n/UFNSZsHAAA + headers: + content-encoding: [gzip] + content-length: ['1194'] + content-type: [application/json] + status: {code: 200, message: OK} +version: 1 From 9fb57975a00d35861d30bb7299b67114a2857f3d Mon Sep 17 00:00:00 2001 From: Changaco Date: Wed, 16 Jul 2014 23:36:43 +0200 Subject: [PATCH 36/97] failing test for #916 --- tests/py/test_billing_payday.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/py/test_billing_payday.py b/tests/py/test_billing_payday.py index b14a8e9cf1..2cd2fa2736 100644 --- a/tests/py/test_billing_payday.py +++ b/tests/py/test_billing_payday.py @@ -291,6 +291,19 @@ def test_card_hold_error(self, Customer, fch): payday = self.fetch_payday() assert payday['ncc_failing'] == 1 + def test_payin_doesnt_process_tips_when_goal_is_negative(self): + alice = self.make_participant('alice', claimed_time='now', balance=20) + bob = self.make_participant('bob', claimed_time='now') + alice.set_tip_to(bob, 13) + self.db.run("UPDATE participants SET goal = -1 WHERE username='bob'") + payday = Payday.start() + with self.db.get_cursor() as cursor: + payday.prepare(cursor, payday.ts_start) + payday.transfer_tips(cursor) + payday.update_balances(cursor) + assert Participant.from_id(alice.id).balance == 20 + assert Participant.from_id(bob.id).balance == 0 + def test_transfer_takes(self): a_team = self.make_participant('a_team', claimed_time='now', number='plural', balance=20) alice = self.make_participant('alice', claimed_time='now') From ac1decd0a39a4e10e3a708d573d790f5a3292d3c Mon Sep 17 00:00:00 2001 From: Changaco Date: Wed, 16 Jul 2014 23:45:01 +0200 Subject: [PATCH 37/97] don't transfer tips to participants who don't want to receive them fixes #916 --- gittip/billing/payday.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/gittip/billing/payday.py b/gittip/billing/payday.py index 22fd98cc8e..2e741f1696 100644 --- a/gittip/billing/payday.py +++ b/gittip/billing/payday.py @@ -137,6 +137,7 @@ def prepare(cursor, ts_start): , balanced_customer_href , last_bill_result , is_suspicious + , goal FROM participants WHERE is_suspicious IS NOT true AND claimed_time < %(ts_start)s @@ -160,8 +161,9 @@ def prepare(cursor, ts_start): ORDER BY tipper, tippee, mtime DESC ) t JOIN pay_participants p ON p.username = t.tipper + JOIN pay_participants p2 ON p2.username = t.tippee WHERE t.amount > 0 - AND t.tippee IN (SELECT username FROM pay_participants) + AND (p2.goal IS NULL or p2.goal >= 0) AND ( SELECT id FROM pay_transfers t2 WHERE t.tipper = t2.tipper From 618a38bb6adb3fba911b1c778f0583c4c40afd6a Mon Sep 17 00:00:00 2001 From: Changaco Date: Sat, 26 Jul 2014 11:40:14 +0200 Subject: [PATCH 38/97] small refactor --- gittip/billing/exchanges.py | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/gittip/billing/exchanges.py b/gittip/billing/exchanges.py index 1e7a69ffd3..f5cc473e5c 100644 --- a/gittip/billing/exchanges.py +++ b/gittip/billing/exchanges.py @@ -3,7 +3,6 @@ from __future__ import unicode_literals from decimal import Decimal, ROUND_UP -import sys import balanced @@ -44,6 +43,13 @@ def skim_credit(amount): return amount - FEE_CREDIT, FEE_CREDIT +def repr_exception(e): + if isinstance(e, balanced.exc.HTTPError): + return e.message.message + else: + return repr(e) + + def ach_credit(db, participant, withhold, minimum_credit=MINIMUM_CREDIT): # Compute the amount to credit them. @@ -106,12 +112,8 @@ def ach_credit(db, participant, withhold, minimum_credit=MINIMUM_CREDIT): log(msg + "succeeded.") error = "" - except balanced.exc.HTTPError as err: - error = err.message.message - except: - error = repr(sys.exc_info()[1]) - - if error: + except Exception as e: + error = repr_exception(e) log(msg + "failed: %s" % error) record_exchange(db, 'ach', -credit_amount, fee, error, participant) @@ -160,12 +162,8 @@ def create_card_hold(db, participant, amount): ) log(msg + "succeeded.") error = "" - except balanced.exc.HTTPError as err: - error = err.message.message - except: - error = repr(sys.exc_info()[1]) - - if error: + except Exception as e: + error = repr_exception(e) log(msg + "failed: %s" % error) record_exchange(db, 'bill', None, None, error, participant) From b0fd116a2b6ba37718ee058e017f2eaa4c41ad90 Mon Sep 17 00:00:00 2001 From: Changaco Date: Fri, 25 Jul 2014 14:03:13 +0200 Subject: [PATCH 39/97] update balance *before* a withdrawal --- branch.sql | 5 ++ gittip/billing/exchanges.py | 106 +++++++++++++++++++++++------------- 2 files changed, 73 insertions(+), 38 deletions(-) diff --git a/branch.sql b/branch.sql index dbbfe4e047..a5d7df7f67 100644 --- a/branch.sql +++ b/branch.sql @@ -1,4 +1,9 @@ BEGIN; + ALTER TABLE paydays ADD COLUMN stage integer DEFAULT 0; ALTER TABLE participants DROP COLUMN pending; + + CREATE TYPE exchange_status AS ENUM ('pre', 'pending', 'failed', 'succeeded'); + ALTER TABLE exchanges ADD COLUMN status exchange_status; + END; diff --git a/gittip/billing/exchanges.py b/gittip/billing/exchanges.py index f5cc473e5c..3561047eac 100644 --- a/gittip/billing/exchanges.py +++ b/gittip/billing/exchanges.py @@ -104,19 +104,20 @@ def ach_credit(db, participant, withhold, minimum_credit=MINIMUM_CREDIT): # Try to dance with Balanced. # =========================== + e_id = record_exchange(db, 'ach', -credit_amount, fee, participant, 'pre') + meta = dict(exchange_id=e_id, participant_id=participant.id) try: customer = balanced.Customer.fetch(balanced_customer_href) - customer.bank_accounts.one()\ - .credit(amount=cents, - description=participant.username) - + ba = customer.bank_accounts.one() + 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) - record_exchange(db, 'ach', -credit_amount, fee, error, participant) return error @@ -165,7 +166,8 @@ def create_card_hold(db, participant, amount): except Exception as e: error = repr_exception(e) log(msg + "failed: %s" % error) - record_exchange(db, 'bill', None, None, error, participant) + db.run("UPDATE participants SET last_bill_result=%s WHERE id=%s", + (error, participant.id)) return hold, error @@ -178,18 +180,26 @@ def capture_card_hold(db, participant, amount, hold): ) username = participant.username + assert participant.id == int(hold.meta['participant_id']) cents, amount_str, charge_amount, fee = _prep_hit(amount) + amount = charge_amount - fee # account for possible rounding + e_id = record_exchange(db, 'bill', amount, fee, participant, 'pre') + + meta = dict(participant_id=participant.id, exchange_id=e_id) + try: + hold.capture(amount=cents, description=username, meta=meta) + record_exchange_result(db, e_id, 'succeeded', None, participant) + except Exception as e: + error = repr_exception(e) + record_exchange_result(db, e_id, 'failed', error, participant) + raise - hold.capture(amount=cents, description=username) hold.meta['state'] = 'captured' hold.save() log("Captured " + amount_str + " on Balanced for " + username) - amount = charge_amount - fee # account for possible rounding - record_exchange(db, 'bill', amount, fee, '', participant) - def cancel_card_hold(hold): """Cancel the previously created hold on the participant's credit card. @@ -227,7 +237,7 @@ def _prep_hit(unrounded): return cents, amount_str, upcharged, fee -def record_exchange(db, kind, amount, fee, error, participant): +def record_exchange(db, kind, amount, fee, participant, status): """Given a Bunch of Stuff, return None. Records in the exchanges table have these characteristics: @@ -258,40 +268,60 @@ def record_exchange(db, kind, amount, fee, error, participant): """ - username = participant.username with db.get_cursor() as cursor: - if error: - amount = fee = Decimal('0.00') - else: - EXCHANGE = """\ + exchange_id = cursor.one(""" + INSERT INTO exchanges + (amount, fee, participant, status) + VALUES (%s, %s, %s, %s) + RETURNING id + """, (amount, fee, participant.username, status)) - INSERT INTO exchanges - (amount, fee, participant) - VALUES (%s, %s, %s) + if amount < 0: + amount -= fee + propagate_exchange(cursor, participant, kind, '', amount) - """ - cursor.execute(EXCHANGE, (amount, fee, username)) + return exchange_id - # Update the participant's balance. - # ================================= - RESULT = """\ +def record_exchange_result(db, exchange_id, status, error, participant): + """Updates the status of an exchange. + """ + with db.get_cursor() as cursor: + amount, fee, username = cursor.one(""" + UPDATE exchanges + SET status=%(status)s + , note=%(error)s + WHERE id=%(exchange_id)s + AND status <> %(status)s + RETURNING amount, fee, participant + """, locals()) + assert participant.username == username + + if amount < 0: + amount -= fee + amount = amount if error else 0 + propagate_exchange(cursor, participant, 'ach', error, -amount) + else: + amount = amount if not error else 0 + propagate_exchange(cursor, participant, 'bill', error, amount) - UPDATE participants - SET last_{0}_result=%s - , balance=(balance + %s) - WHERE username=%s - RETURNING balance - """.format(kind) - if kind == 'ach': - amount -= fee - balance = cursor.one(RESULT, (error or '', amount, username)) - if balance < 0: - raise NegativeBalance +def propagate_exchange(cursor, participant, kind, error, amount): + """Propagates an exchange to the participant's balance. + """ + column = 'last_%s_result' % kind + error = error or '' + new_balance = cursor.one(""" + UPDATE participants + SET {0}=%s + , balance=(balance + %s) + WHERE id=%s + RETURNING balance + """.format(column), (error, amount, participant.id)) + + if amount < 0 and new_balance < 0: + raise NegativeBalance if hasattr(participant, 'set_attributes'): - attrs = dict(balance=balance) - attrs['last_%s_result' % kind] = error or '' - participant.set_attributes(**attrs) + participant.set_attributes(**{'balance': new_balance, column: error}) From 06d66e03155c9bd984eb3d8c217ac15869241dbb Mon Sep 17 00:00:00 2001 From: Changaco Date: Fri, 25 Jul 2014 14:09:51 +0200 Subject: [PATCH 40/97] update DB self check --- gittip/models/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gittip/models/__init__.py b/gittip/models/__init__.py index 6d7f4d7d9b..859986aaef 100644 --- a/gittip/models/__init__.py +++ b/gittip/models/__init__.py @@ -62,6 +62,7 @@ def _check_balances(cursor): select participant as username, sum(amount) as a from exchanges where amount > 0 + and (status is null or status = 'succeeded') group by participant union all @@ -69,6 +70,7 @@ def _check_balances(cursor): select participant as username, sum(amount-fee) as a from exchanges where amount < 0 + and (status is null or status <> 'failed') group by participant union all From 8fd9a11dcb28ad2b38e11b2571164b972214a8b4 Mon Sep 17 00:00:00 2001 From: Changaco Date: Fri, 25 Jul 2014 14:10:39 +0200 Subject: [PATCH 41/97] update tests --- tests/py/test_billing_exchanges.py | 53 ++++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 14 deletions(-) diff --git a/tests/py/test_billing_exchanges.py b/tests/py/test_billing_exchanges.py index d4e07b6483..dd8e79a032 100644 --- a/tests/py/test_billing_exchanges.py +++ b/tests/py/test_billing_exchanges.py @@ -14,6 +14,7 @@ capture_card_hold, create_card_hold, record_exchange, + record_exchange_result, skim_credit, ) from gittip.exceptions import NegativeBalance, NoBalancedCustomerHref, NotWhitelisted @@ -252,17 +253,29 @@ def test_skim_credit(self): class TestRecordExchange(Harness): - def test_record_exchange_updates_balance(self): + def test_record_exchange_doesnt_update_balance_for_positive_amounts(self): alice = self.make_participant('alice') record_exchange( self.db , 'bill' , amount=D("0.59") , fee=D("0.41") - , error="" , participant=alice + , status='pre' ) alice = Participant.from_username('alice') - assert alice.balance == D("0.59") + assert alice.balance == D('0.00') + + def test_record_exchange_updates_balance_for_negative_amounts(self): + alice = self.make_participant('alice', balance=50) + record_exchange( self.db + , 'ach' + , amount=D('-35.84') + , fee=D('0.75') + , participant=alice + , status='pre' + ) + alice = Participant.from_username('alice') + assert alice.balance == D('13.41') def test_record_exchange_fails_if_negative_balance(self): alice = self.make_participant('alice') @@ -272,18 +285,30 @@ def test_record_exchange_fails_if_negative_balance(self): , 'ach' , amount=D("-10.00") , fee=D("0.41") - , error="" , participant=alice + , status='pre' ) - def test_record_exchange_doesnt_update_balance_if_error(self): - alice = self.make_participant('alice') - record_exchange( self.db - , 'bill' - , amount=D("1.00") - , fee=D("0.41") - , error="SOME ERROR" - , participant=alice - ) + def test_record_exchange_result_restores_balance_on_error(self): + alice = self.make_participant('alice', balance=30) + e_id = record_exchange(self.db, 'ach', 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) + alice = Participant.from_username('alice') + assert alice.balance == D('30.00') + + def test_record_exchange_result_doesnt_restore_balance_on_success(self): + alice = self.make_participant('alice', balance=50) + e_id = record_exchange(self.db, 'ach', D('-43.98'), D('1.60'), alice, 'pre') + assert alice.balance == D('4.42') + record_exchange_result( self.db, e_id, 'succeeded', None, alice) + alice = Participant.from_username('alice') + assert alice.balance == D('4.42') + + def test_record_exchange_result_updates_balance_for_positive_amounts(self): + alice = self.make_participant('alice', balance=4) + e_id = record_exchange(self.db, 'bill', D('31.59'), D('0.01'), alice, 'pre') + assert alice.balance == D('4.00') + record_exchange_result( self.db, e_id, 'succeeded', None, alice) alice = Participant.from_username('alice') - assert alice.balance == D("0.00") + assert alice.balance == D('35.59') From 0fe101eb16db3462341d43df71c97f338f323249 Mon Sep 17 00:00:00 2001 From: Changaco Date: Mon, 28 Jul 2014 18:35:10 +0200 Subject: [PATCH 42/97] sync with Balanced before starting payday (fixes #213) --- gittip/billing/exchanges.py | 44 +++++++++++++++++++++++-------------- gittip/cli.py | 2 ++ 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/gittip/billing/exchanges.py b/gittip/billing/exchanges.py index 3561047eac..251ea93a39 100644 --- a/gittip/billing/exchanges.py +++ b/gittip/billing/exchanges.py @@ -9,6 +9,7 @@ from aspen import log from aspen.utils import typecheck from gittip.exceptions import NegativeBalance, NoBalancedCustomerHref, NotWhitelisted +from gittip.models.participant import Participant # Balanced has a $0.50 minimum. We go even higher to avoid onerous @@ -249,23 +250,6 @@ def record_exchange(db, kind, amount, fee, participant, status): fee The payment processor's fee. It's always positive. - This function takes the result of an API call to a payment processor - and records the result in our db. If the power goes out at this point - then Postgres will be out of sync with the payment processor. We'll - have to resolve that manually be reviewing the transaction log at the - processor and modifying Postgres accordingly. - - For Balanced, this could be automated by generating an ID locally and - commiting that to the db and then passing that through in the meta - field.* Then syncing would be a case of simply:: - - for payment in unresolved_payments: - payment_in_balanced = balanced.Transaction.query.filter( - **{'meta.unique_id': 'value'}).one() - payment.transaction_uri = payment_in_balanced.uri - - * https://www.balancedpayments.com/docs/meta - """ with db.get_cursor() as cursor: @@ -325,3 +309,29 @@ def propagate_exchange(cursor, participant, kind, error, amount): if hasattr(participant, 'set_attributes'): participant.set_attributes(**{'balance': new_balance, column: error}) + + +def sync_with_balanced(db): + """We can get out of sync with Balanced if record_exchange_result was + interrupted or wasn't called. This is where we fix that. + """ + exchanges = db.all(""" + SELECT * + FROM exchanges + WHERE status = 'pre' + """) + meta_exchange_id = balanced.Transaction.f.meta.exchange_id + for e in exchanges: + p = Participant.from_username(e.participant) + cls = balanced.Debit if e.amount > 0 else balanced.Credit + transactions = cls.query.filter(meta_exchange_id == e.id).all() + assert len(transactions) < 2 + if transactions: + t = transactions[0] + error = t.failure_reason + status = t.status + assert (not error) ^ (status == 'failed') + record_exchange_result(db, e.id, status, error, p) + else: + # The exchange didn't happen, just remove it + db.run("DELETE FROM exchanges WHERE id=%s", (e.id,)) diff --git a/gittip/cli.py b/gittip/cli.py index c448ac2b56..bd1dccc585 100644 --- a/gittip/cli.py +++ b/gittip/cli.py @@ -26,9 +26,11 @@ def payday(): # This dodges a problem where db in billing is None if we import it from # gittip before calling wireup.billing. + from gittip.billing.exchanges import sync_with_balanced from gittip.billing.payday import Payday try: + sync_with_balanced() Payday.start().run() except KeyboardInterrupt: pass From 8d36bfb475f00a3f3c1cca3cea669f3d36fdfd5d Mon Sep 17 00:00:00 2001 From: Changaco Date: Mon, 28 Jul 2014 18:35:43 +0200 Subject: [PATCH 43/97] add tests for `sync_with_balanced()` --- gittip/testing/__init__.py | 1 + tests/py/test_billing_exchanges.py | 41 +++++++++++++++++++++++++++++- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/gittip/testing/__init__.py b/gittip/testing/__init__.py index bf3a4d0872..f36d4644b4 100644 --- a/gittip/testing/__init__.py +++ b/gittip/testing/__init__.py @@ -63,6 +63,7 @@ class Harness(unittest.TestCase): @classmethod def setUpClass(cls): + cls.db.run("ALTER SEQUENCE exchanges_id_seq RESTART WITH 1") cls.setUpVCR() diff --git a/tests/py/test_billing_exchanges.py b/tests/py/test_billing_exchanges.py index dd8e79a032..49ee420da5 100644 --- a/tests/py/test_billing_exchanges.py +++ b/tests/py/test_billing_exchanges.py @@ -16,10 +16,11 @@ record_exchange, record_exchange_result, skim_credit, + sync_with_balanced, ) from gittip.exceptions import NegativeBalance, NoBalancedCustomerHref, NotWhitelisted from gittip.models.participant import Participant -from gittip.testing import Harness, raise_foobar +from gittip.testing import Foobar, Harness, raise_foobar from gittip.testing.balanced import BalancedHarness @@ -312,3 +313,41 @@ def test_record_exchange_result_updates_balance_for_positive_amounts(self): record_exchange_result( self.db, e_id, 'succeeded', None, alice) alice = Participant.from_username('alice') assert alice.balance == D('35.59') + + +class TestSyncWithBalanced(BalancedHarness): + + @classmethod + def tearDownClass(cls): + has_exchange_id = balanced.Transaction.f.meta.contains('exchange_id') + for t in balanced.Debit.query.filter(has_exchange_id): + t.meta.pop('exchange_id') + t.save() + super(TestSyncWithBalanced, cls).tearDownClass() + + def test_sync_with_balanced(self): + with mock.patch('gittip.billing.exchanges.record_exchange_result') as rer: + rer.side_effect = Foobar() + hold, error = create_card_hold(self.db, self.janet, D('20.00')) + assert error == '' # sanity check + with self.assertRaises(Foobar): + capture_card_hold(self.db, self.janet, D('10.00'), hold) + exchange = self.db.one("SELECT * FROM exchanges") + assert exchange.status == 'pre' + sync_with_balanced(self.db) + exchange = self.db.one("SELECT * FROM exchanges") + assert exchange.status == 'succeeded' + + def test_sync_with_balanced_deletes_exchanges_that_didnt_happen(self): + with mock.patch('gittip.billing.exchanges.record_exchange_result') as rer \ + , mock.patch('balanced.CardHold.capture') as capture: + rer.side_effect = capture.side_effect = Foobar + hold, error = create_card_hold(self.db, self.janet, D('33.67')) + assert error == '' # sanity check + with self.assertRaises(Foobar): + capture_card_hold(self.db, self.janet, D('7.52'), hold) + exchange = self.db.one("SELECT * FROM exchanges") + assert exchange.status == 'pre' + sync_with_balanced(self.db) + exchanges = self.db.all("SELECT * FROM exchanges") + assert not exchanges From d14bbafdf4c2335caf84ea8ef83293f83dde739a Mon Sep 17 00:00:00 2001 From: Changaco Date: Tue, 29 Jul 2014 10:38:16 +0200 Subject: [PATCH 44/97] add test fixture --- tests/py/fixtures/TestSyncWithBalanced.yml | 304 +++++++++++++++++++++ 1 file changed, 304 insertions(+) create mode 100644 tests/py/fixtures/TestSyncWithBalanced.yml diff --git a/tests/py/fixtures/TestSyncWithBalanced.yml b/tests/py/fixtures/TestSyncWithBalanced.yml new file mode 100644 index 0000000000..652c6a409a --- /dev/null +++ b/tests/py/fixtures/TestSyncWithBalanced.yml @@ -0,0 +1,304 @@ +interactions: +- request: + body: null + headers: {} + method: GET + uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd + response: + body: + string: !!binary | + H4sIAAAAAAAAA41Uy27bMBC85ysEnWvLipQ29i1oby2CAG3RokUh0CQLEZFIhY+ggqF/79KWZJpU + wlx02J0ZcmdHPFwlSYqN0qKlUqW75DcUkuRw/EKLo5ZClZumeTfVGsYfLXQCAUwJI7EPhDqhSjOO + NBN8VBlFhlnNdARpSiqkAZFeb/Jytfmw2uTf8ptdWezKcp0Xxfvb7a90pmBJX6UU622+uSlKh0LE + vmoF17U/DCP22I/fi88/Sf98//UT7b88FN3dD7mtn8j5zK4W3B8wrSX9a+nZ7GAWVwKjcY24rpRG + 2lgjUy5WLdK4Ph/XUo08i+mTheZp4KCdradI+qMhQiRV3qYw070HhD3BSun1cjkPy/bivhcg0glo + NBUWZKGJheFa9m43GGRvFNxDqWopdUrxqkFKl/6YtEWsCYrsInADnPXH5sdO6oT3HP31HOE0A9eO + eVbZYV7s2B/GFTlEjCSpatGQ4yqdKDhkRobMwZ2S7GjsEX+sED6aFJG5hAZKhO5ZTGLELHDdv/UF + H5xfesEM+k9TySEFbxsmhAeXsrZFLDlBAiZhqjOaRsgzKuBribhC2D5eEY0LZKADj4ThsRkm0AL7 + GV5m1ESuIOkECxSEJKe3/ZVwjpiAC08tieZpAsH/NVwN/wHQiK4AUwYAAA== + headers: + content-encoding: [gzip] + content-length: ['499'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: null + headers: {} + method: GET + uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd/cards?limit=10&offset=0 + response: + body: + string: !!binary | + H4sIAAAAAAAAA6VVW2+bMBR+76+weNhTmwBOyEWqprSbVq1NWq1dO3WakIPtxiqxmW2yoCr/fbaB + AL1o08YDEud+vvOdw9MBAF6CJFbeFHw3HwA8ubcRp4w/WnEtsJa50mJNpJF6p1/h+TdcbBbXH0hx + cQWz2Z2crH5ir/LfHdaBKOMPRGaScW0dx8mI+oMQwmiI6HicQAKjhPrhaBCRBEUDihMKI4yTkAQU + Tow5SSYUUor9IQqjkbcPnGw2sSQqT21cnqfpXsPz9bIsc9t6AvM03mij4kwojdJ4jXSysrVx0ejJ + NmMSaSZ4vBZcW33g7zOsiUZdcCR5MLYOGsFpruxHbW6xY7qItfjlLNoKhLFpQsWhVZyILQhfQsiw + i3sK52cn6i7anKELqPKLW3xzu7yctBBBmjwIWVhroVcGgn3BusiIFSeSYKa7INb9dzBcIv4Yc7R2 + Xp+v5pdfPs0W4PRsdv0RnMwW54dg0Zv1ungqLQnRDZ4FUY3BUiLu+rhlCjXiPMOmahwjx47QDwZH + /ujID26C4XQAp4NhbzwMQhjct1KVkBn7NjkNwEbS7qFkMbHIviIObD4TGcwR4+Dald4ejKGGds2/ + Os+KOonAzmbih4Hf9k5EzrUs4srA5n+xGQniMSZLM44p0DInDX9L2DvzaPGxIMjuYOiHDSHNMjxr + 02Mq3hDJKCMW9k4CS/796nhl76BiIjBjAmV7wFYPsABcaOBY0hq4rb5i0xRQlKqm/pUk1KLSd7el + /xfEleQPJBhEcBLA+3I3dgbKHxYtr7OHXoqUY1G/vlQm9xt3qiztfcrWTB8H/jtBqSL6uJ6hx8m2 + e1a8pqnqDP57cJfVFFofFK/MbiTVQL1Mkg0TuT3ADQs8Lcy5sn4lUzzK5P83bGB0x/rZxS//Cz3M + VJZrs8fNOJ/cVHsM7/p7bVVQqWn/J1qjqPxq7a5alyqRdY1XInX/opo5rVQtfSeZW6A3fCqd7fBg + 9xv4QHhu7QYAAA== + headers: + content-encoding: [gzip] + content-length: ['751'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: '{"amount": 2091, "meta": {"state": "new", "participant_id": 2}, "description": + "janet"}' + headers: {} + method: POST + uri: https://api.balancedpayments.com:443/cards/CC3MHBsW6vHaL3suLVdTVbO9/card_holds + response: + body: {string: !!python/unicode "{\n \"card_holds\": [\n {\n \"status\": + \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"card\": + \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \"debit\": null\n },\n \"updated_at\": + \"2014-07-28T16:30:02.921362Z\",\n \"created_at\": \"2014-07-28T16:30:02.711192Z\",\n + \ \"transaction_number\": \"HL871-342-0127\",\n \"expires_at\": \"2014-08-04T16:30:02.831423Z\",\n + \ \"failure_reason\": null,\n \"currency\": \"USD\",\n \"amount\": + 2091,\n \"meta\": {\n \"state\": \"new\",\n \"participant_id\": + \"2\"\n },\n \"href\": \"/card_holds/HL3jv8PCtkEc3Ekb45O5Lj9K\",\n + \ \"failure_reason_code\": null,\n \"voided_at\": null,\n \"id\": + \"HL3jv8PCtkEc3Ekb45O5Lj9K\"\n }\n ],\n \"links\": {\n \"card_holds.events\": + \"/card_holds/{card_holds.id}/events\",\n \"card_holds.card\": \"/cards/{card_holds.card}\",\n + \ \"card_holds.debits\": \"/card_holds/{card_holds.id}/debits\",\n \"card_holds.debit\": + \"/debits/{card_holds.debit}\"\n }\n}"} + headers: + content-length: ['944'] + content-type: [application/json] + status: {code: 201, message: CREATED} +- request: + body: '{"amount": 1061, "meta": {"exchange_id": 1, "participant_id": 2}, "description": + "janet"}' + headers: {} + method: POST + uri: https://api.balancedpayments.com:443/card_holds/HL3jv8PCtkEc3Ekb45O5Lj9K/debits + response: + body: {string: !!python/unicode "{\n \"debits\": [\n {\n \"status\": + \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"customer\": + \"CU3KXdyvNSDeyLP3pAWr9hqd\",\n \"source\": \"CC3MHBsW6vHaL3suLVdTVbO9\",\n + \ \"order\": null,\n \"dispute\": null\n },\n \"updated_at\": + \"2014-07-28T16:30:03.744922Z\",\n \"created_at\": \"2014-07-28T16:30:03.302729Z\",\n + \ \"transaction_number\": \"W230-378-7591\",\n \"failure_reason\": + null,\n \"currency\": \"USD\",\n \"amount\": 1061,\n \"failure_reason_code\": + null,\n \"meta\": {\n \"exchange_id\": \"1\",\n \"participant_id\": + \"2\"\n },\n \"href\": \"/debits/WD3k9QXZZl0ET7hWnCkZJnVP\",\n \"appears_on_statement_as\": + \"BAL*example.com\",\n \"id\": \"WD3k9QXZZl0ET7hWnCkZJnVP\"\n }\n + \ ],\n \"links\": {\n \"debits.customer\": \"/customers/{debits.customer}\",\n + \ \"debits.dispute\": \"/disputes/{debits.dispute}\",\n \"debits.source\": + \"/resources/{debits.source}\",\n \"debits.order\": \"/orders/{debits.order}\",\n + \ \"debits.refunds\": \"/debits/{debits.id}/refunds\",\n \"debits.events\": + \"/debits/{debits.id}/events\"\n }\n}"} + headers: + content-length: ['1067'] + content-type: [application/json] + status: {code: 201, message: CREATED} +- request: + body: null + headers: {} + method: GET + uri: https://api.balancedpayments.com:443/debits?meta.exchange_id=1&limit=25&offset=0 + response: + body: + string: !!binary | + H4sIAAAAAAAAA51U32/aMBB+71+B8tCHaUB+UNIgoamFSdVGt06lUDFNkYmP4ZE4me0gEMr/Ptsx + mCBVk/oS+e5899333cWHq1bLyUAgZ9A6yLO0UsSFtJwuhiUR/FNKMiKG/s21utaBXbJG9DfEBA+9 + 63y14iCGrvOxzqWwU7m0TFPjWTNYvbeaRpbJ/o0pVsNJj2scBYMtyUvexBS5QKl0eebWirB3UJKM + KlXASQndKASjTy1LJym5yDNgmtzR4N3DRbg6SmP8mPCiFFBLUp9tkgle5sAWqFAdHGdyQiG46pqo + 4WpgcoZNa/pkIbR5CSBnVFL8JsIx3ITgecmSmgiD2rAwtV05JxHrviSHn3pTai2luFwgoefn8DJJ + ADBgo5gMYuAJI4UgOVU3/iAKwkabc9G7ez6U0Uvw9RXvt9+ex7CfPAXF3ZxF67+2vEI/cRiNgseH + ez7vbx/QJODlZIans+X36AQnbx9FteutQe1IVUDTM6ujw2WBkQAcI/1X+a7Xa7th27+dev1B4A7c + oBP2epHvLyyzhMF/UgLXD/3oLEUwRDlKlFQxLbNlPf25H7jtILxthzeRZ+uvEElLBrGE4Vrac0ZS + RMaAJnvV7svz2KahLC+pYuG5ffNvSX7NYnGSY7UUjYqNB0aLcvaMKBjbm4wViAmSkAJRIV8ZLZra + o6aqF+9Kdz4ONtGP18UidT9Pw/WcjjaLL3T2dNZ9UQBiPJYCqaWDTP5UMdKrd383+QA7lBUpdJI8 + szk1/Ju1dVeV/P66qv4BRDUHNUoFAAA= + headers: + content-encoding: [gzip] + content-length: ['593'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: null + headers: {} + method: GET + uri: https://api.balancedpayments.com:443/debits?meta.exchange_id=1&limit=25&offset=0 + response: + body: + string: !!binary | + H4sIAAAAAAAAA51U32/aMBB+71+B8tCHaUB+UNIgoamFSdVGt06lUDFNkYmP4ZE4me0gEMr/Ptsx + mCBVk/oS+e5899333cWHq1bLyUAgZ9A6yLO0UsSFtJwuhiUR/FNKMiKG/s21utaBXbJG9DfEBA+9 + 63y14iCGrvOxzqWwU7m0TFPjWTNYvbeaRpbJ/o0pVsNJj2scBYMtyUvexBS5QKl0eebWirB3UJKM + KlXASQndKASjTy1LJym5yDNgmtzR4N3DRbg6SmP8mPCiFFBLUp9tkgle5sAWqFAdHGdyQiG46pqo + 4WpgcoZNa/pkIbR5CSBnVFL8JsIx3ITgecmSmgiD2rAwtV05JxHrviSHn3pTai2luFwgoefn8DJJ + ADBgo5gMYuAJI4UgOVU3/iAKwkabc9G7ez6U0Uvw9RXvt9+ex7CfPAXF3ZxF67+2vEI/cRiNgseH + ez7vbx/QJODlZIans+X36AQnbx9FteutQe1IVUDTM6ujw2WBkQAcI/1X+a7Xa7th27+dev1B4A7c + oBP2epHvLyyzhMF/UgLXD/3oLEUwRDlKlFQxLbNlPf25H7jtILxthzeRZ+uvEElLBrGE4Vrac0ZS + RMaAJnvV7svz2KahLC+pYuG5ffNvSX7NYnGSY7UUjYqNB0aLcvaMKBjbm4wViAmSkAJRIV8ZLZra + o6aqF+9Kdz4ONtGP18UidT9Pw/WcjjaLL3T2dNZ9UQBiPJYCqaWDTP5UMdKrd383+QA7lBUpdJI8 + szk1/Ju1dVeV/P66qv4BRDUHNUoFAAA= + headers: + content-encoding: [gzip] + content-length: ['593'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: null + headers: {} + method: GET + uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd + response: + body: + string: !!binary | + H4sIAAAAAAAAA41Uy27bMBC85ysEnWvLipQ29i1oby2CAG3RokUh0CQLEZFIhY+ggqF/79KWZJpU + wlx02J0ZcmdHPFwlSYqN0qKlUqW75DcUkuRw/EKLo5ZClZumeTfVGsYfLXQCAUwJI7EPhDqhSjOO + NBN8VBlFhlnNdARpSiqkAZFeb/Jytfmw2uTf8ptdWezKcp0Xxfvb7a90pmBJX6UU622+uSlKh0LE + vmoF17U/DCP22I/fi88/Sf98//UT7b88FN3dD7mtn8j5zK4W3B8wrSX9a+nZ7GAWVwKjcY24rpRG + 2lgjUy5WLdK4Ph/XUo08i+mTheZp4KCdradI+qMhQiRV3qYw070HhD3BSun1cjkPy/bivhcg0glo + NBUWZKGJheFa9m43GGRvFNxDqWopdUrxqkFKl/6YtEWsCYrsInADnPXH5sdO6oT3HP31HOE0A9eO + eVbZYV7s2B/GFTlEjCSpatGQ4yqdKDhkRobMwZ2S7GjsEX+sED6aFJG5hAZKhO5ZTGLELHDdv/UF + H5xfesEM+k9TySEFbxsmhAeXsrZFLDlBAiZhqjOaRsgzKuBribhC2D5eEY0LZKADj4ThsRkm0AL7 + GV5m1ESuIOkECxSEJKe3/ZVwjpiAC08tieZpAsH/NVwN/wHQiK4AUwYAAA== + headers: + content-encoding: [gzip] + content-length: ['499'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: null + headers: {} + method: GET + uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd/cards?limit=10&offset=0 + response: + body: + string: !!binary | + H4sIAAAAAAAAA6VVW2+bMBR+76+weNhTmwBOyEWqprSbVq1NWq1dO3WakIPtxiqxmW2yoCr/fbaB + AL1o08YDEud+vvOdw9MBAF6CJFbeFHw3HwA8ubcRp4w/WnEtsJa50mJNpJF6p1/h+TdcbBbXH0hx + cQWz2Z2crH5ir/LfHdaBKOMPRGaScW0dx8mI+oMQwmiI6HicQAKjhPrhaBCRBEUDihMKI4yTkAQU + Tow5SSYUUor9IQqjkbcPnGw2sSQqT21cnqfpXsPz9bIsc9t6AvM03mij4kwojdJ4jXSysrVx0ejJ + NmMSaSZ4vBZcW33g7zOsiUZdcCR5MLYOGsFpruxHbW6xY7qItfjlLNoKhLFpQsWhVZyILQhfQsiw + i3sK52cn6i7anKELqPKLW3xzu7yctBBBmjwIWVhroVcGgn3BusiIFSeSYKa7INb9dzBcIv4Yc7R2 + Xp+v5pdfPs0W4PRsdv0RnMwW54dg0Zv1ungqLQnRDZ4FUY3BUiLu+rhlCjXiPMOmahwjx47QDwZH + /ujID26C4XQAp4NhbzwMQhjct1KVkBn7NjkNwEbS7qFkMbHIviIObD4TGcwR4+Dald4ejKGGds2/ + Os+KOonAzmbih4Hf9k5EzrUs4srA5n+xGQniMSZLM44p0DInDX9L2DvzaPGxIMjuYOiHDSHNMjxr + 02Mq3hDJKCMW9k4CS/796nhl76BiIjBjAmV7wFYPsABcaOBY0hq4rb5i0xRQlKqm/pUk1KLSd7el + /xfEleQPJBhEcBLA+3I3dgbKHxYtr7OHXoqUY1G/vlQm9xt3qiztfcrWTB8H/jtBqSL6uJ6hx8m2 + e1a8pqnqDP57cJfVFFofFK/MbiTVQL1Mkg0TuT3ADQs8Lcy5sn4lUzzK5P83bGB0x/rZxS//Cz3M + VJZrs8fNOJ/cVHsM7/p7bVVQqWn/J1qjqPxq7a5alyqRdY1XInX/opo5rVQtfSeZW6A3fCqd7fBg + 9xv4QHhu7QYAAA== + headers: + content-encoding: [gzip] + content-length: ['751'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: '{"amount": 3499, "meta": {"state": "new", "participant_id": 2}, "description": + "janet"}' + headers: {} + method: POST + uri: https://api.balancedpayments.com:443/cards/CC3MHBsW6vHaL3suLVdTVbO9/card_holds + response: + body: {string: !!python/unicode "{\n \"card_holds\": [\n {\n \"status\": + \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"card\": + \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \"debit\": null\n },\n \"updated_at\": + \"2014-07-28T16:30:06.083389Z\",\n \"created_at\": \"2014-07-28T16:30:05.863795Z\",\n + \ \"transaction_number\": \"HL795-707-8407\",\n \"expires_at\": \"2014-08-04T16:30:05.996087Z\",\n + \ \"failure_reason\": null,\n \"currency\": \"USD\",\n \"amount\": + 3499,\n \"meta\": {\n \"state\": \"new\",\n \"participant_id\": + \"2\"\n },\n \"href\": \"/card_holds/HL3n2zl9c0239MjedLlvsS7b\",\n + \ \"failure_reason_code\": null,\n \"voided_at\": null,\n \"id\": + \"HL3n2zl9c0239MjedLlvsS7b\"\n }\n ],\n \"links\": {\n \"card_holds.events\": + \"/card_holds/{card_holds.id}/events\",\n \"card_holds.card\": \"/cards/{card_holds.card}\",\n + \ \"card_holds.debits\": \"/card_holds/{card_holds.id}/debits\",\n \"card_holds.debit\": + \"/debits/{card_holds.debit}\"\n }\n}"} + headers: + content-length: ['944'] + content-type: [application/json] + status: {code: 201, message: CREATED} +- request: + body: null + headers: {} + method: GET + uri: https://api.balancedpayments.com:443/debits?meta.exchange_id=2&limit=25&offset=0 + response: + body: {string: !!python/unicode "{\n \"meta\": {\n \"last\": \"/debits?limit=25&meta.exchange_id=2&offset=0\",\n + \ \"next\": null,\n \"href\": \"/debits?limit=25&meta.exchange_id=2&offset=0\",\n + \ \"limit\": 25,\n \"offset\": 0,\n \"previous\": null,\n \"total\": + 0,\n \"first\": \"/debits?limit=25&meta.exchange_id=2&offset=0\"\n },\n + \ \"links\": {},\n \"debits\": []\n}"} + headers: + content-length: ['320'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: null + headers: {} + method: GET + uri: https://api.balancedpayments.com:443/debits?meta.exchange_id=2&limit=25&offset=0 + response: + body: {string: !!python/unicode "{\n \"meta\": {\n \"last\": \"/debits?limit=25&meta.exchange_id=2&offset=0\",\n + \ \"next\": null,\n \"href\": \"/debits?limit=25&meta.exchange_id=2&offset=0\",\n + \ \"limit\": 25,\n \"offset\": 0,\n \"previous\": null,\n \"total\": + 0,\n \"first\": \"/debits?limit=25&meta.exchange_id=2&offset=0\"\n },\n + \ \"links\": {},\n \"debits\": []\n}"} + headers: + content-length: ['320'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: null + headers: {} + method: GET + uri: https://api.balancedpayments.com:443/debits?meta%5Bcontains%5D=exchange_id&limit=25&offset=0 + response: + body: + string: !!binary | + H4sIAAAAAAAAA61UUW/aMBB+76+oIrUP04CQAGmQ0FRgUrWxrVMpVExTZOxjeCROajsIhPLfZzuG + EKRqD9tL5Lvz3Xffdxcfrq6vnQQkcvrXB3VWVoyEVJbTIrCkUnyIaULlwOve6ms33SFOmUSUiZvu + eAA7vEbsF0SU3KarlQA5cJ33ZR0GO12H5XFsPWsOq/9R2XSkCnldW7iEVh7XOjIOW5rmoo4vU4li + 5WrbWyvK/5GqYlroYk5M2UajWQ1L6Zo4FzJNgBvSR0O0Dhfh4iiZ9RMqslxCKVV5rpJs8DIHtsCk + 7uA4txMKJUXLRi1vC5NyYlszpwrCmJcAanY5I28iHMN1CJHmHJdEOJRGBVPahXMSsexLcfhhNqjU + UokrJJJmlo7IMQYgQKxiKkhAYE4zSVOmb/xGDGQVrc/F7Pf5UEbP/ucXst9+fRrDfvLoZ/dzHq5f + q/Ia/cRhNPK/PAzFvLd9QBNf5JMZmc6W38ITnLp9FLVaewNajVQHDD27OiacZwRJIBEyf57ntjsN + N2h4d9N2r++7fddvBp1O6HmLihnm8JcU3/UCLzxLkRwxgbCWKmJ5siynP/d8t+EHd42gG7ar+itE + 45xDpGCEkfackRKRc2B4r9t9fhpXaShJc6ZZtN2e/c8Uv3qxCKdEL0WtYu0RMqKcPS4apupNxTLE + JcU0Q0yqt8eIpveorurFe9Oaj/1N+P1lsYjdj9NgPWejzeITmz2edZ9lgLiIlEB66SBRP1WEzOoN + 7yfvYIeSLIYmTpMqp4R/s7bpqlDfn1fFHzIvjmluBQAA + headers: + content-encoding: [gzip] + content-length: ['603'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: '{"status": "succeeded", "source_href": "/resources/CC3MHBsW6vHaL3suLVdTVbO9", + "transaction_number": "W230-378-7591", "description": "janet", "created_at": + "2014-07-28T16:30:03.302729Z", "appears_on_statement_as": "BAL*example.com", + "updated_at": "2014-07-28T16:30:03.744922Z", "order": null, "currency": "USD", + "amount": 1061, "meta": {"participant_id": "2"}, "customer_href": "/customers/CU3KXdyvNSDeyLP3pAWr9hqd", + "failure_reason_code": null, "failure_reason": null, "id": "WD3k9QXZZl0ET7hWnCkZJnVP", + "dispute": null}' + headers: {} + method: PUT + uri: https://api.balancedpayments.com:443/debits/WD3k9QXZZl0ET7hWnCkZJnVP + response: + body: + string: !!binary | + H4sIAAAAAAAAA31SXW/aMBR9769AeZwG+RqE8NbCpGpjW6dSUjFNkbFvhUfiZP5AQyj/fXa+nDJ1 + L1bse8+595ycy81o5BDYUymcxeiHvo1Gl/rU70Iiqcy7IxTGAASI874rEhCY01LSgpmOX4iBtNWM + sqNBdlyaDSshixy46V4+hZ+fyfn09XEF5/VDWN4mPD78tvRmeqE4hrp7GX65vxPJ7HSP1qFQ6y3Z + bPff4n6c7i44qamZyrJuRyONilJJw2IK7e5VL0KVBEkgKZJmTuD5H8ZeNA7mG3+2CL2FF03i+Xwa + THdWGebwX0g4Cb0gCuIBRHLEBMLGqpSpfN94kAShNw6j+Tiaxr7lf0E0UxxSPUbU1g4VaRM5B4bP + Zt2nx5WFobxQzKjwvZnf63tNluKCdFb0LTlIpGGDH1UiLimmJWIypaQ2xvnHuQOHF1Nym/C4ySo8 + xt+fd7vM+7iJDglbHnef2PZhsGFZAuIi1SaYYEEOmh/V8bq7Xb+DPygvM5jgIreYZvyb3PVWlT5/ + GjnO69C1sZ4Mc+d2F+FemsX7ctVO7WA2OVpjkyILah+uMTayLofmYjHN/RrS5dZx6y/bXl+vu7Xp + ipHas874TgcllR7alJuf2ymBk3b6TUxb1f5VN9VfK2gqtg8EAAA= + headers: + content-encoding: [gzip] + content-length: ['494'] + content-type: [application/json] + status: {code: 200, message: OK} +version: 1 From 2f44b9046ef6836c9ffbb15dc1473c228b2f65c1 Mon Sep 17 00:00:00 2001 From: Changaco Date: Tue, 29 Jul 2014 10:38:42 +0200 Subject: [PATCH 45/97] failing test for #1853 --- tests/py/test_billing_payday.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/py/test_billing_payday.py b/tests/py/test_billing_payday.py index 2cd2fa2736..20463765f0 100644 --- a/tests/py/test_billing_payday.py +++ b/tests/py/test_billing_payday.py @@ -335,6 +335,23 @@ def test_transfer_takes(self): else: assert p.balance == 0 + def test_take_over_during_payin(self): + alice = self.make_participant('alice', claimed_time='now', balance=50) + bob = self.make_participant('bob', claimed_time='now', elsewhere='twitter') + alice.set_tip_to(bob, 18) + payday = Payday.start() + with self.db.get_cursor() as cursor: + payday.prepare(cursor, payday.ts_start) + bruce = self.make_participant('bruce', claimed_time='now') + bruce.take_over(('twitter', str(bob.id)), have_confirmation=True) + payday.transfer_tips(cursor) + billy = self.make_participant('billy', claimed_time='now') + billy.take_over(('github', str(bruce.id)), have_confirmation=True) + payday.update_balances(cursor) + assert Participant.from_id(bob.id).balance == 0 + assert Participant.from_id(bruce.id).balance == 0 + assert Participant.from_id(billy.id).balance == 18 + class TestPayout(Harness): From 82b1012d5bffbc46a819c91ba69de37944941cd5 Mon Sep 17 00:00:00 2001 From: Changaco Date: Tue, 29 Jul 2014 11:42:54 +0200 Subject: [PATCH 46/97] fix #1853 --- gittip/billing/payday.py | 55 ++++++++++++++++++++++++++++++++- tests/py/test_billing_payday.py | 2 ++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/gittip/billing/payday.py b/gittip/billing/payday.py index 2e741f1696..75b1678a6e 100644 --- a/gittip/billing/payday.py +++ b/gittip/billing/payday.py @@ -11,6 +11,8 @@ """ from __future__ import unicode_literals +import itertools + from balanced import CardHold import aspen.utils @@ -117,6 +119,7 @@ def payin(self): self.settle_card_holds(cursor, holds) self.update_balances(cursor) check_db(cursor) + self.take_over_balances() @staticmethod @@ -205,7 +208,17 @@ def prepare(cursor, ts_start): WHERE username = $2; INSERT INTO transfers (tipper, tippee, amount, context) - VALUES ($1, $2, $3, $4); + VALUES ( ( SELECT p.username + FROM participants p + JOIN pay_participants p2 ON p.id = p2.id + WHERE p2.username = $1 ) + , ( SELECT p.username + FROM participants p + JOIN pay_participants p2 ON p.id = p2.id + WHERE p2.username = $2 ) + , $3 + , $4 + ); END; $$ LANGUAGE plpgsql; @@ -432,6 +445,46 @@ def update_balances(cursor): log("Updated the balances of %i participants." % len(participants)) + def take_over_balances(self): + """If an account that receives money is taken over during payin we need + to transfer the balance to the absorbing account. + """ + for i in itertools.count(): + if i > 10: + raise Exception('possible infinite loop') + count = self.db.one(""" + + DROP TABLE IF EXISTS temp; + CREATE TEMPORARY TABLE temp AS + SELECT archived_as, absorbed_by, balance AS archived_balance + FROM absorptions a + JOIN participants p ON a.archived_as = p.username + WHERE balance > 0; + + SELECT count(*) FROM temp; + + """) + if not count: + break + self.db.run(""" + + INSERT INTO transfers (tipper, tippee, amount, context) + SELECT archived_as, absorbed_by, archived_balance, 'take-over' + FROM temp; + + UPDATE participants + SET balance = (balance - archived_balance) + FROM temp + WHERE username = archived_as; + + UPDATE participants + SET balance = (balance + archived_balance) + FROM temp + WHERE username = absorbed_by; + + """) + + def payout(self): """This is the second stage of payday in which we send money out to the bank accounts of participants. diff --git a/tests/py/test_billing_payday.py b/tests/py/test_billing_payday.py index 20463765f0..a0e67e3280 100644 --- a/tests/py/test_billing_payday.py +++ b/tests/py/test_billing_payday.py @@ -345,9 +345,11 @@ def test_take_over_during_payin(self): bruce = self.make_participant('bruce', claimed_time='now') bruce.take_over(('twitter', str(bob.id)), have_confirmation=True) payday.transfer_tips(cursor) + bruce.delete_elsewhere('twitter', str(bob.id)) billy = self.make_participant('billy', claimed_time='now') billy.take_over(('github', str(bruce.id)), have_confirmation=True) payday.update_balances(cursor) + payday.take_over_balances() assert Participant.from_id(bob.id).balance == 0 assert Participant.from_id(bruce.id).balance == 0 assert Participant.from_id(billy.id).balance == 18 From b6a55ad6098e8fd8d6e4e63f9cf164949f3a7fa4 Mon Sep 17 00:00:00 2001 From: Changaco Date: Wed, 30 Jul 2014 10:54:08 +0200 Subject: [PATCH 47/97] fix missing argument --- gittip/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gittip/cli.py b/gittip/cli.py index bd1dccc585..70eafc80bc 100644 --- a/gittip/cli.py +++ b/gittip/cli.py @@ -30,7 +30,7 @@ def payday(): from gittip.billing.payday import Payday try: - sync_with_balanced() + sync_with_balanced(db) Payday.start().run() except KeyboardInterrupt: pass From 606ee2ebfe3cc004608642dd7dbe9c273d51741d Mon Sep 17 00:00:00 2001 From: Chad Whitacre Date: Wed, 30 Jul 2014 11:32:02 -0400 Subject: [PATCH 48/97] Document the call structure of Payday.run --- gittip/billing/payday.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/gittip/billing/payday.py b/gittip/billing/payday.py index 75b1678a6e..f02f115b8c 100644 --- a/gittip/billing/payday.py +++ b/gittip/billing/payday.py @@ -39,6 +39,22 @@ class Payday(object): want to use their balance at the start of Payday. Balance changes should be atomic globally per-Payday. + Here's the call structure of the Payday.run method: + + run + payin + prepare + create_card_holds + transfer_tips + transfer_takes + settle_card_holds + update_balances + take_over_balances + payout + update_stats + update_receiving_amounts + end + """ From 0dad8385ba70b4ed78a798bbf4660618c385459f Mon Sep 17 00:00:00 2001 From: Changaco Date: Wed, 30 Jul 2014 17:38:33 +0200 Subject: [PATCH 49/97] fix query to exclude participants who aren't whitelisted --- gittip/billing/payday.py | 1 + 1 file changed, 1 insertion(+) diff --git a/gittip/billing/payday.py b/gittip/billing/payday.py index f02f115b8c..96c4832833 100644 --- a/gittip/billing/payday.py +++ b/gittip/billing/payday.py @@ -329,6 +329,7 @@ def create_card_holds(self, cursor): WHERE old_balance < giving_today AND balanced_customer_href IS NOT NULL AND last_bill_result IS NOT NULL + AND is_suspicious IS false """) if not participants: return {} From 772b5b3be4dc6e6d4b5a5b88a3f73d3d6b75a90e Mon Sep 17 00:00:00 2001 From: Chad Whitacre Date: Wed, 30 Jul 2014 11:43:34 -0400 Subject: [PATCH 50/97] Fix log message at end of payday script The aspen.utils.to_age function actually uses old-style string formatting. --- gittip/billing/payday.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gittip/billing/payday.py b/gittip/billing/payday.py index 96c4832833..e1e88151c5 100644 --- a/gittip/billing/payday.py +++ b/gittip/billing/payday.py @@ -119,7 +119,7 @@ def run(self): _end = aspen.utils.utcnow() _delta = _end - _start - fmt_past = "Script ran for {age} (%s)." % _delta + fmt_past = "Script ran for %%(age)s (%s)." % _delta log(aspen.utils.to_age(_start, fmt_past=fmt_past)) From e4a198aa9f9e4dece6e8d288bc0b50a20ead4238 Mon Sep 17 00:00:00 2001 From: Changaco Date: Wed, 30 Jul 2014 17:46:01 +0200 Subject: [PATCH 51/97] remove the `database_maxconn` restriction from `payday()` --- gittip/cli.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/gittip/cli.py b/gittip/cli.py index 70eafc80bc..17f18c63af 100644 --- a/gittip/cli.py +++ b/gittip/cli.py @@ -7,13 +7,8 @@ def payday(): # Wire things up. # =============== - # Manually override max db connections so that we only have one connection. - # Our db access is serialized right now anyway, and with only one - # connection it's easier to trust changes to statement_timeout. The point - # here is that we want to turn off statement_timeout for payday. env = wireup.env() - env.database_maxconn = 1 db = wireup.db(env) db.run("SET statement_timeout = 0") From d20081f7f2964f8e51c8cd513a79af96cd4769d0 Mon Sep 17 00:00:00 2001 From: Changaco Date: Wed, 30 Jul 2014 18:03:14 +0200 Subject: [PATCH 52/97] also remove our meddling with `statement_timeout` --- gittip/cli.py | 1 - 1 file changed, 1 deletion(-) diff --git a/gittip/cli.py b/gittip/cli.py index 17f18c63af..5773166011 100644 --- a/gittip/cli.py +++ b/gittip/cli.py @@ -10,7 +10,6 @@ def payday(): env = wireup.env() db = wireup.db(env) - db.run("SET statement_timeout = 0") wireup.billing(env) wireup.nanswers(env) From cd2e901cfa258883dfe941e27b133aa8d258a7f4 Mon Sep 17 00:00:00 2001 From: Changaco Date: Wed, 30 Jul 2014 18:51:15 +0200 Subject: [PATCH 53/97] rewrite query in `payout()` to better filter participants --- gittip/billing/payday.py | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/gittip/billing/payday.py b/gittip/billing/payday.py index e1e88151c5..c49a777cc0 100644 --- a/gittip/billing/payday.py +++ b/gittip/billing/payday.py @@ -20,9 +20,7 @@ from gittip.billing.exchanges import ( ach_credit, cancel_card_hold, capture_card_hold, create_card_hold, upcharge ) -from gittip.exceptions import ( - NegativeBalance, NoBalancedCustomerHref, NotWhitelisted -) +from gittip.exceptions import NegativeBalance from gittip.models import check_db from psycopg2 import IntegrityError @@ -509,19 +507,20 @@ def payout(self): i = 0 log("Starting payout loop.") participants = self.db.all(""" - SELECT p.*::participants FROM participants p WHERE balance > 0 + SELECT p.*::participants + FROM participants p + WHERE balance > 0 + AND balanced_customer_href IS NOT NULL + AND last_ach_result IS NOT NULL """) for i, participant in enumerate(participants, start=1): - withhold = participant.giving + participant.pledging - try: - error = ach_credit(self.db, participant, withhold) - if error: - self.mark_ach_failed() - except NoBalancedCustomerHref: + if participant.is_suspicious is None: + log("UNREVIEWED: %s" % participant.username) continue - except NotWhitelisted: - if participant.is_suspicious is None: - log("UNREVIEWED: %s" % participant.username) + withhold = participant.giving + participant.pledging + error = ach_credit(self.db, participant, withhold) + if error: + self.mark_ach_failed() log("Did payout for %d participants." % i) self.db.self_check() log("Checked the DB.") From b8c37d70b6658c40754d78b3c0f54cced14e3b57 Mon Sep 17 00:00:00 2001 From: Changaco Date: Wed, 30 Jul 2014 19:09:57 +0200 Subject: [PATCH 54/97] fix test --- tests/py/test_billing_payday.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/py/test_billing_payday.py b/tests/py/test_billing_payday.py index a0e67e3280..c648f47221 100644 --- a/tests/py/test_billing_payday.py +++ b/tests/py/test_billing_payday.py @@ -365,7 +365,8 @@ def test_payout_no_balanced_href(self): @mock.patch('gittip.billing.payday.ach_credit') def test_payout_ach_error(self, ach_credit): self.make_participant('alice', claimed_time='now', is_suspicious=False, - balance=20) + balance=20, balanced_customer_href='foo', + last_ach_result='') ach_credit.return_value = 'some error' Payday.start().payout() payday = self.fetch_payday() From bcb43bf00ab7a8958c0a8172fa5121edcb76bc14 Mon Sep 17 00:00:00 2001 From: Changaco Date: Thu, 31 Jul 2014 10:17:47 +0200 Subject: [PATCH 55/97] improve error message of DB self check --- gittip/models/__init__.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/gittip/models/__init__.py b/gittip/models/__init__.py index 859986aaef..922f04b650 100644 --- a/gittip/models/__init__.py +++ b/gittip/models/__init__.py @@ -54,10 +54,10 @@ def _check_balances(cursor): https://github.com/gittip/www.gittip.com/issues/1118 """ - b = cursor.one(""" - select count(*) + b = cursor.all(""" + select p.username, expected, balance as actual from ( - select username, sum(a) as balance + select username, sum(a) as expected from ( select participant as username, sum(amount) as a from exchanges @@ -86,14 +86,11 @@ def _check_balances(cursor): group by tippee ) as foo group by username - - except - - select username, balance - from participants ) as foo2 + join participants p on p.username = foo2.username + where expected <> p.balance """) - assert b == 0, "conflicting balances: {}".format(b) + assert len(b) == 0, "conflicting balances: {}".format(b) def _check_orphans(cursor): From 8c93e68cdfe07653bd3b63b7c94cd0fb20b0498a Mon Sep 17 00:00:00 2001 From: Changaco Date: Thu, 31 Jul 2014 11:06:34 +0200 Subject: [PATCH 56/97] add helper function for making exchanges in tests --- gittip/testing/__init__.py | 7 +++++++ tests/py/test_billing.py | 9 +++------ tests/py/test_billing_payday.py | 8 ++------ tests/py/test_charts_json.py | 12 ++++-------- tests/py/test_history.py | 14 +++++++------- 5 files changed, 23 insertions(+), 27 deletions(-) diff --git a/gittip/testing/__init__.py b/gittip/testing/__init__.py index f36d4644b4..9a0e6d7630 100644 --- a/gittip/testing/__init__.py +++ b/gittip/testing/__init__.py @@ -11,6 +11,7 @@ from aspen import resources from aspen.utils import utcnow from aspen.testing.client import Client +from gittip.billing.exchanges import record_exchange, record_exchange_result from gittip.elsewhere import UserInfo from gittip.models.account_elsewhere import AccountElsewhere from gittip.security.user import User, SESSION @@ -204,6 +205,12 @@ def fetch_payday(self): return self.db.one("SELECT * FROM paydays", back_as=dict) + def make_exchange(self, kind, amount, fee, participant): + e_id = record_exchange(self.db, kind, amount, fee, participant, 'pre') + record_exchange_result(self.db, e_id, 'succeeded', '', participant) + return e_id + + class Foobar(Exception): pass diff --git a/tests/py/test_billing.py b/tests/py/test_billing.py index 15dd358935..5df59f72ae 100644 --- a/tests/py/test_billing.py +++ b/tests/py/test_billing.py @@ -91,13 +91,10 @@ def test_receipt_page_loads (self): ).save() card.associate_to_customer(customer_href) - self.make_participant('alice', claimed_time='now', balanced_customer_href=customer_href) + alice = self.make_participant('alice', claimed_time='now', + balanced_customer_href=customer_href) - ex_id = self.db.one("insert into exchanges " - "(amount, fee, participant) " - "values (113.00, 30.00, 'alice') " - "returning id" - ) + ex_id = self.make_exchange('bill', 113, 30, alice) url_receipt = '/alice/receipts/{}.html'.format(ex_id) actual = self.client.GET(url_receipt, auth_as='alice').body.decode('utf8') assert 'Visa' in actual diff --git a/tests/py/test_billing_payday.py b/tests/py/test_billing_payday.py index c648f47221..729f930c56 100644 --- a/tests/py/test_billing_payday.py +++ b/tests/py/test_billing_payday.py @@ -252,12 +252,8 @@ def test_fetch_card_holds_handles_extra_holds(self, cancel, CardHold): def test_payin_cancels_uncaptured_holds(self, log): self.janet.set_tip_to(self.homer, 42) alice = self.make_participant('alice', claimed_time='now', - is_suspicious=False, balance=50) - self.db.run(""" - INSERT INTO exchanges - (amount, fee, participant) - VALUES (50, 0, 'alice'); - """) + is_suspicious=False) + self.make_exchange('bill', 50, 0, alice) alice.set_tip_to(self.janet, 50) Payday.start().payin() assert log.call_args_list[-3][0] == ("Captured 0 card holds.",) diff --git a/tests/py/test_charts_json.py b/tests/py/test_charts_json.py index 33dfb4ad59..f423d1d5ba 100644 --- a/tests/py/test_charts_json.py +++ b/tests/py/test_charts_json.py @@ -16,15 +16,11 @@ class TestChartsJson(Harness): def setUp(self): Harness.setUp(self) - self.alice = self.make_participant('alice', balance=10, claimed_time='now') - self.bob = self.make_participant('bob', balance=10, claimed_time='now') + self.alice = self.make_participant('alice', claimed_time='now') + self.bob = self.make_participant('bob', claimed_time='now') self.carl = self.make_participant('carl', claimed_time='now') - self.db.run(""" - INSERT INTO EXCHANGES - (amount, fee, participant) VALUES - (10.00, 0.00, 'alice'), - (10.00, 0.00, 'bob') - """) + self.make_exchange('bill', 10, 0, self.alice) + self.make_exchange('bill', 10, 0, self.bob) self.make_participant('notactive', claimed_time='now') self.alice.set_tip_to(self.carl, '1.00') diff --git a/tests/py/test_history.py b/tests/py/test_history.py index 4b63022353..8282d41538 100644 --- a/tests/py/test_history.py +++ b/tests/py/test_history.py @@ -14,14 +14,14 @@ class TestHistory(Harness): def test_iter_payday_events(self): Payday.start().run() - team = self.make_participant('team', number='plural', claimed_time='now', balance=10000) - alice = self.make_participant('alice', claimed_time='now', balance=5000) + team = self.make_participant('team', number='plural', claimed_time='now') + alice = self.make_participant('alice', claimed_time='now') + self.make_exchange('bill', 10000, 0, team) + self.make_exchange('bill', 10000, 0, alice) + self.make_exchange('bill', -5000, 0, alice) self.db.run(""" - INSERT INTO exchanges - (amount, fee, participant, timestamp) - VALUES (10000, 0, 'team', now() - interval '1 month') - , (10000, 0, 'alice', now() - interval '1 month') - , (-5000, 0, 'alice', now() - interval '1 month'); + UPDATE transfers + SET timestamp = "timestamp" - interval '1 month' """) bob = self.make_participant('bob', claimed_time='now') carl = self.make_participant('carl', claimed_time='now') From dc85043dd4437243636b26d7bbd6faef66415dd2 Mon Sep 17 00:00:00 2001 From: Changaco Date: Thu, 31 Jul 2014 11:12:57 +0200 Subject: [PATCH 57/97] check DB before and after `sync_with_balanced()` --- gittip/billing/exchanges.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/gittip/billing/exchanges.py b/gittip/billing/exchanges.py index 251ea93a39..cc3a7ba82d 100644 --- a/gittip/billing/exchanges.py +++ b/gittip/billing/exchanges.py @@ -9,6 +9,7 @@ from aspen import log from aspen.utils import typecheck from gittip.exceptions import NegativeBalance, NoBalancedCustomerHref, NotWhitelisted +from gittip.models import check_db from gittip.models.participant import Participant @@ -315,6 +316,7 @@ def sync_with_balanced(db): """We can get out of sync with Balanced if record_exchange_result was interrupted or wasn't called. This is where we fix that. """ + check_db(db) exchanges = db.all(""" SELECT * FROM exchanges @@ -335,3 +337,4 @@ def sync_with_balanced(db): else: # The exchange didn't happen, just remove it db.run("DELETE FROM exchanges WHERE id=%s", (e.id,)) + check_db(db) From 3bc3b5c1275ef85576481b38cb61d29c37fcff8f Mon Sep 17 00:00:00 2001 From: Changaco Date: Thu, 31 Jul 2014 11:14:04 +0200 Subject: [PATCH 58/97] failing test for `sync_with_balanced()` --- tests/py/test_billing_exchanges.py | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/tests/py/test_billing_exchanges.py b/tests/py/test_billing_exchanges.py index 49ee420da5..0ba21c729f 100644 --- a/tests/py/test_billing_exchanges.py +++ b/tests/py/test_billing_exchanges.py @@ -1,6 +1,7 @@ from __future__ import absolute_import, division, print_function, unicode_literals from decimal import Decimal as D +import itertools import balanced import mock @@ -320,7 +321,9 @@ class TestSyncWithBalanced(BalancedHarness): @classmethod def tearDownClass(cls): has_exchange_id = balanced.Transaction.f.meta.contains('exchange_id') - for t in balanced.Debit.query.filter(has_exchange_id): + credits = balanced.Credit.query.filter(has_exchange_id) + debits = balanced.Debit.query.filter(has_exchange_id) + for t in itertools.chain(credits, debits): t.meta.pop('exchange_id') t.save() super(TestSyncWithBalanced, cls).tearDownClass() @@ -337,8 +340,9 @@ def test_sync_with_balanced(self): sync_with_balanced(self.db) exchange = self.db.one("SELECT * FROM exchanges") assert exchange.status == 'succeeded' + assert Participant.from_username('janet').balance == 10 - def test_sync_with_balanced_deletes_exchanges_that_didnt_happen(self): + def test_sync_with_balanced_deletes_charges_that_didnt_happen(self): with mock.patch('gittip.billing.exchanges.record_exchange_result') as rer \ , mock.patch('balanced.CardHold.capture') as capture: rer.side_effect = capture.side_effect = Foobar @@ -351,3 +355,18 @@ def test_sync_with_balanced_deletes_exchanges_that_didnt_happen(self): sync_with_balanced(self.db) exchanges = self.db.all("SELECT * FROM exchanges") assert not exchanges + assert Participant.from_username('janet').balance == 0 + + def test_sync_with_balanced_reverts_credits_that_didnt_happen(self): + self.make_exchange('bill', 41, 0, self.homer) + with mock.patch('gittip.billing.exchanges.record_exchange_result') as rer \ + , mock.patch('balanced.Customer.fetch') as fetch: + rer.side_effect = fetch.side_effect = Foobar + with self.assertRaises(Foobar): + ach_credit(self.db, self.homer, 0, 0) + exchange = self.db.one("SELECT * FROM exchanges WHERE amount < 0") + assert exchange.status == 'pre' + sync_with_balanced(self.db) + exchanges = self.db.all("SELECT * FROM exchanges WHERE amount < 0") + assert not exchanges + assert Participant.from_username('homer').balance == 41 From 35293294b116f965b3a4bd6741f406c66ddff374 Mon Sep 17 00:00:00 2001 From: Changaco Date: Thu, 31 Jul 2014 11:55:28 +0200 Subject: [PATCH 59/97] fix `sync_with_balanced()` --- gittip/billing/exchanges.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/gittip/billing/exchanges.py b/gittip/billing/exchanges.py index cc3a7ba82d..143ff7a997 100644 --- a/gittip/billing/exchanges.py +++ b/gittip/billing/exchanges.py @@ -335,6 +335,13 @@ def sync_with_balanced(db): assert (not error) ^ (status == 'failed') record_exchange_result(db, e.id, status, error, p) else: - # The exchange didn't happen, just remove it + # The exchange didn't happen, remove it db.run("DELETE FROM exchanges WHERE id=%s", (e.id,)) + # and restore the participant's balance if it was a credit + if e.amount < 0: + db.run(""" + UPDATE participants + SET balance=(balance + %s) + WHERE id=%s + """, (-e.amount + e.fee, p.id)) check_db(db) From 0e7a1a316b5bb775c4c9591122f7da498645ea09 Mon Sep 17 00:00:00 2001 From: Changaco Date: Thu, 31 Jul 2014 11:56:22 +0200 Subject: [PATCH 60/97] update test fixture --- tests/py/fixtures/TestSyncWithBalanced.yml | 174 +++++++++++++-------- 1 file changed, 110 insertions(+), 64 deletions(-) diff --git a/tests/py/fixtures/TestSyncWithBalanced.yml b/tests/py/fixtures/TestSyncWithBalanced.yml index 652c6a409a..98f4cb64a5 100644 --- a/tests/py/fixtures/TestSyncWithBalanced.yml +++ b/tests/py/fixtures/TestSyncWithBalanced.yml @@ -58,13 +58,13 @@ interactions: body: {string: !!python/unicode "{\n \"card_holds\": [\n {\n \"status\": \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"card\": \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \"debit\": null\n },\n \"updated_at\": - \"2014-07-28T16:30:02.921362Z\",\n \"created_at\": \"2014-07-28T16:30:02.711192Z\",\n - \ \"transaction_number\": \"HL871-342-0127\",\n \"expires_at\": \"2014-08-04T16:30:02.831423Z\",\n + \"2014-07-31T09:51:18.956318Z\",\n \"created_at\": \"2014-07-31T09:51:18.731562Z\",\n + \ \"transaction_number\": \"HL595-244-2487\",\n \"expires_at\": \"2014-08-07T09:51:18.863860Z\",\n \ \"failure_reason\": null,\n \"currency\": \"USD\",\n \"amount\": 2091,\n \"meta\": {\n \"state\": \"new\",\n \"participant_id\": - \"2\"\n },\n \"href\": \"/card_holds/HL3jv8PCtkEc3Ekb45O5Lj9K\",\n + \"2\"\n },\n \"href\": \"/card_holds/HL1IjkJ7ps5zFIuHwAfD6E24\",\n \ \"failure_reason_code\": null,\n \"voided_at\": null,\n \"id\": - \"HL3jv8PCtkEc3Ekb45O5Lj9K\"\n }\n ],\n \"links\": {\n \"card_holds.events\": + \"HL1IjkJ7ps5zFIuHwAfD6E24\"\n }\n ],\n \"links\": {\n \"card_holds.events\": \"/card_holds/{card_holds.id}/events\",\n \"card_holds.card\": \"/cards/{card_holds.card}\",\n \ \"card_holds.debits\": \"/card_holds/{card_holds.id}/debits\",\n \"card_holds.debit\": \"/debits/{card_holds.debit}\"\n }\n}"} @@ -77,18 +77,18 @@ interactions: "janet"}' headers: {} method: POST - uri: https://api.balancedpayments.com:443/card_holds/HL3jv8PCtkEc3Ekb45O5Lj9K/debits + uri: https://api.balancedpayments.com:443/card_holds/HL1IjkJ7ps5zFIuHwAfD6E24/debits response: body: {string: !!python/unicode "{\n \"debits\": [\n {\n \"status\": \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"customer\": \"CU3KXdyvNSDeyLP3pAWr9hqd\",\n \"source\": \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \ \"order\": null,\n \"dispute\": null\n },\n \"updated_at\": - \"2014-07-28T16:30:03.744922Z\",\n \"created_at\": \"2014-07-28T16:30:03.302729Z\",\n - \ \"transaction_number\": \"W230-378-7591\",\n \"failure_reason\": + \"2014-07-31T09:51:19.768970Z\",\n \"created_at\": \"2014-07-31T09:51:19.303392Z\",\n + \ \"transaction_number\": \"W741-253-2155\",\n \"failure_reason\": null,\n \"currency\": \"USD\",\n \"amount\": 1061,\n \"failure_reason_code\": null,\n \"meta\": {\n \"exchange_id\": \"1\",\n \"participant_id\": - \"2\"\n },\n \"href\": \"/debits/WD3k9QXZZl0ET7hWnCkZJnVP\",\n \"appears_on_statement_as\": - \"BAL*example.com\",\n \"id\": \"WD3k9QXZZl0ET7hWnCkZJnVP\"\n }\n + \"2\"\n },\n \"href\": \"/debits/WD1IXiXjgMLtXAZSpgzrbB0d\",\n \"appears_on_statement_as\": + \"BAL*example.com\",\n \"id\": \"WD1IXiXjgMLtXAZSpgzrbB0d\"\n }\n \ ],\n \"links\": {\n \"debits.customer\": \"/customers/{debits.customer}\",\n \ \"debits.dispute\": \"/disputes/{debits.dispute}\",\n \"debits.source\": \"/resources/{debits.source}\",\n \"debits.order\": \"/orders/{debits.order}\",\n @@ -106,20 +106,20 @@ interactions: response: body: string: !!binary | - H4sIAAAAAAAAA51U32/aMBB+71+B8tCHaUB+UNIgoamFSdVGt06lUDFNkYmP4ZE4me0gEMr/Ptsx - mCBVk/oS+e5899333cWHq1bLyUAgZ9A6yLO0UsSFtJwuhiUR/FNKMiKG/s21utaBXbJG9DfEBA+9 - 63y14iCGrvOxzqWwU7m0TFPjWTNYvbeaRpbJ/o0pVsNJj2scBYMtyUvexBS5QKl0eebWirB3UJKM - KlXASQndKASjTy1LJym5yDNgmtzR4N3DRbg6SmP8mPCiFFBLUp9tkgle5sAWqFAdHGdyQiG46pqo - 4WpgcoZNa/pkIbR5CSBnVFL8JsIx3ITgecmSmgiD2rAwtV05JxHrviSHn3pTai2luFwgoefn8DJJ - ADBgo5gMYuAJI4UgOVU3/iAKwkabc9G7ez6U0Uvw9RXvt9+ex7CfPAXF3ZxF67+2vEI/cRiNgseH - ez7vbx/QJODlZIans+X36AQnbx9FteutQe1IVUDTM6ujw2WBkQAcI/1X+a7Xa7th27+dev1B4A7c - oBP2epHvLyyzhMF/UgLXD/3oLEUwRDlKlFQxLbNlPf25H7jtILxthzeRZ+uvEElLBrGE4Vrac0ZS - RMaAJnvV7svz2KahLC+pYuG5ffNvSX7NYnGSY7UUjYqNB0aLcvaMKBjbm4wViAmSkAJRIV8ZLZra - o6aqF+9Kdz4ONtGP18UidT9Pw/WcjjaLL3T2dNZ9UQBiPJYCqaWDTP5UMdKrd383+QA7lBUpdJI8 - szk1/Ju1dVeV/P66qv4BRDUHNUoFAAA= + H4sIAAAAAAAAA51U247aMBB9369AediHqkAuCzRIqOLysFXZthK7C9qqikw8gLeJk/qCoCj/Xtsx + JEFaVdqXyDPjmTPnzMSnm1bLSUEgZ9g6qbOyEsSFspwuhjUR/HNCUiJGfu9WX+vAId4huoWI4JF3 + m202HMTIdT6WuRQOOpfKJLGeHYPNe6sZZJXs92yxEk55XOvIGexJJnkTU2QCJcrl2Vsbwt5BSTEq + dAEnIfS3RrD6lLJ0YslFlgIz5M4G756uwsVZGuvHhOdSQClJea6SbPA6B/ZAhe7gPJMLCsFF10Yt + VwuTMWxbM6cKwpjXAGpGkuI3Ec7hJgTPJItLIgxKo4Ip7cK5iFj2pTj8NJtSaqnE5QIJMz+HyzgG + wICtYiqIgceM5IJkVN94RRREFW3OxexufSjTp+DrCh/33xYzOM5/BPl4ycLdn6q8Rr9wmE6Dh/sJ + X/b392gecDl/xo/P6+/hBU7dPotarbcBrUaqA4aeXR0TljlGAnCEzF/lu95d2x20A+/RDYc9b+iF + nUH/UzhwXypmMYP/pARuEIR+LUUwRDmKtVQRlem6nP5ycOe1/V7Q9r1er6q/QSSRDCIFw420dUZK + RMaAxkfd7tNiVqWhNJNUs/Dcvv23FL9msSjOsF6KRsXGA2NEqT0jGsary5wjJkhMckSFemWMaHqP + mqpevSvd5cz7siKr1+3DXKzGL4t8+5etJ25tmVCeA2I8UgLppYNU/VQRMqs3Gc8/wAGleQKdOEsr + xiX8m7VNV4X6/rop/gGWtguCSgUAAA== headers: content-encoding: [gzip] - content-length: ['593'] + content-length: ['592'] content-type: [application/json] status: {code: 200, message: OK} - request: @@ -130,20 +130,20 @@ interactions: response: body: string: !!binary | - H4sIAAAAAAAAA51U32/aMBB+71+B8tCHaUB+UNIgoamFSdVGt06lUDFNkYmP4ZE4me0gEMr/Ptsx - mCBVk/oS+e5899333cWHq1bLyUAgZ9A6yLO0UsSFtJwuhiUR/FNKMiKG/s21utaBXbJG9DfEBA+9 - 63y14iCGrvOxzqWwU7m0TFPjWTNYvbeaRpbJ/o0pVsNJj2scBYMtyUvexBS5QKl0eebWirB3UJKM - KlXASQndKASjTy1LJym5yDNgmtzR4N3DRbg6SmP8mPCiFFBLUp9tkgle5sAWqFAdHGdyQiG46pqo - 4WpgcoZNa/pkIbR5CSBnVFL8JsIx3ITgecmSmgiD2rAwtV05JxHrviSHn3pTai2luFwgoefn8DJJ - ADBgo5gMYuAJI4UgOVU3/iAKwkabc9G7ez6U0Uvw9RXvt9+ex7CfPAXF3ZxF67+2vEI/cRiNgseH - ez7vbx/QJODlZIans+X36AQnbx9FteutQe1IVUDTM6ujw2WBkQAcI/1X+a7Xa7th27+dev1B4A7c - oBP2epHvLyyzhMF/UgLXD/3oLEUwRDlKlFQxLbNlPf25H7jtILxthzeRZ+uvEElLBrGE4Vrac0ZS - RMaAJnvV7svz2KahLC+pYuG5ffNvSX7NYnGSY7UUjYqNB0aLcvaMKBjbm4wViAmSkAJRIV8ZLZra - o6aqF+9Kdz4ONtGP18UidT9Pw/WcjjaLL3T2dNZ9UQBiPJYCqaWDTP5UMdKrd383+QA7lBUpdJI8 - szk1/Ju1dVeV/P66qv4BRDUHNUoFAAA= + H4sIAAAAAAAAA51U247aMBB9369AediHqkAuCzRIqOLysFXZthK7C9qqikw8gLeJk/qCoCj/Xtsx + JEFaVdqXyDPjmTPnzMSnm1bLSUEgZ9g6qbOyEsSFspwuhjUR/HNCUiJGfu9WX+vAId4huoWI4JF3 + m202HMTIdT6WuRQOOpfKJLGeHYPNe6sZZJXs92yxEk55XOvIGexJJnkTU2QCJcrl2Vsbwt5BSTEq + dAEnIfS3RrD6lLJ0YslFlgIz5M4G756uwsVZGuvHhOdSQClJea6SbPA6B/ZAhe7gPJMLCsFF10Yt + VwuTMWxbM6cKwpjXAGpGkuI3Ec7hJgTPJItLIgxKo4Ip7cK5iFj2pTj8NJtSaqnE5QIJMz+HyzgG + wICtYiqIgceM5IJkVN94RRREFW3OxexufSjTp+DrCh/33xYzOM5/BPl4ycLdn6q8Rr9wmE6Dh/sJ + X/b392gecDl/xo/P6+/hBU7dPotarbcBrUaqA4aeXR0TljlGAnCEzF/lu95d2x20A+/RDYc9b+iF + nUH/UzhwXypmMYP/pARuEIR+LUUwRDmKtVQRlem6nP5ycOe1/V7Q9r1er6q/QSSRDCIFw420dUZK + RMaAxkfd7tNiVqWhNJNUs/Dcvv23FL9msSjOsF6KRsXGA2NEqT0jGsary5wjJkhMckSFemWMaHqP + mqpevSvd5cz7siKr1+3DXKzGL4t8+5etJ25tmVCeA2I8UgLppYNU/VQRMqs3Gc8/wAGleQKdOEsr + xiX8m7VNV4X6/rop/gGWtguCSgUAAA== headers: content-encoding: [gzip] - content-length: ['593'] + content-length: ['592'] content-type: [application/json] status: {code: 200, message: OK} - request: @@ -205,13 +205,13 @@ interactions: body: {string: !!python/unicode "{\n \"card_holds\": [\n {\n \"status\": \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"card\": \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \"debit\": null\n },\n \"updated_at\": - \"2014-07-28T16:30:06.083389Z\",\n \"created_at\": \"2014-07-28T16:30:05.863795Z\",\n - \ \"transaction_number\": \"HL795-707-8407\",\n \"expires_at\": \"2014-08-04T16:30:05.996087Z\",\n + \"2014-07-31T09:51:22.745306Z\",\n \"created_at\": \"2014-07-31T09:51:22.516457Z\",\n + \ \"transaction_number\": \"HL210-211-1689\",\n \"expires_at\": \"2014-08-07T09:51:22.657621Z\",\n \ \"failure_reason\": null,\n \"currency\": \"USD\",\n \"amount\": 3499,\n \"meta\": {\n \"state\": \"new\",\n \"participant_id\": - \"2\"\n },\n \"href\": \"/card_holds/HL3n2zl9c0239MjedLlvsS7b\",\n + \"2\"\n },\n \"href\": \"/card_holds/HL1Mz9q1vB2pmyPdqEiTlEO1\",\n \ \"failure_reason_code\": null,\n \"voided_at\": null,\n \"id\": - \"HL3n2zl9c0239MjedLlvsS7b\"\n }\n ],\n \"links\": {\n \"card_holds.events\": + \"HL1Mz9q1vB2pmyPdqEiTlEO1\"\n }\n ],\n \"links\": {\n \"card_holds.events\": \"/card_holds/{card_holds.id}/events\",\n \"card_holds.card\": \"/cards/{card_holds.card}\",\n \ \"card_holds.debits\": \"/card_holds/{card_holds.id}/debits\",\n \"card_holds.debit\": \"/debits/{card_holds.debit}\"\n }\n}"} @@ -249,6 +249,52 @@ interactions: content-length: ['320'] content-type: [application/json] status: {code: 200, message: OK} +- request: + body: null + headers: {} + method: GET + uri: https://api.balancedpayments.com:443/credits?meta.exchange_id=4&limit=25&offset=0 + response: + body: {string: !!python/unicode "{\n \"credits\": [],\n \"meta\": {\n \"last\": + \"/credits?limit=25&meta.exchange_id=4&offset=0\",\n \"next\": null,\n + \ \"href\": \"/credits?limit=25&meta.exchange_id=4&offset=0\",\n \"limit\": + 25,\n \"offset\": 0,\n \"previous\": null,\n \"total\": 0,\n \"first\": + \"/credits?limit=25&meta.exchange_id=4&offset=0\"\n },\n \"links\": {}\n}"} + headers: + content-length: ['324'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: null + headers: {} + method: GET + uri: https://api.balancedpayments.com:443/credits?meta.exchange_id=4&limit=25&offset=0 + response: + body: {string: !!python/unicode "{\n \"credits\": [],\n \"meta\": {\n \"last\": + \"/credits?limit=25&meta.exchange_id=4&offset=0\",\n \"next\": null,\n + \ \"href\": \"/credits?limit=25&meta.exchange_id=4&offset=0\",\n \"limit\": + 25,\n \"offset\": 0,\n \"previous\": null,\n \"total\": 0,\n \"first\": + \"/credits?limit=25&meta.exchange_id=4&offset=0\"\n },\n \"links\": {}\n}"} + headers: + content-length: ['324'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: null + headers: {} + method: GET + uri: https://api.balancedpayments.com:443/credits?meta%5Bcontains%5D=exchange_id&limit=25&offset=0 + response: + body: {string: !!python/unicode "{\n \"credits\": [],\n \"meta\": {\n \"last\": + \"/credits?limit=25&meta%5Bcontains%5D=exchange_id&offset=0\",\n \"next\": + null,\n \"href\": \"/credits?limit=25&meta%5Bcontains%5D=exchange_id&offset=0\",\n + \ \"limit\": 25,\n \"offset\": 0,\n \"previous\": null,\n \"total\": + 0,\n \"first\": \"/credits?limit=25&meta%5Bcontains%5D=exchange_id&offset=0\"\n + \ },\n \"links\": {}\n}"} + headers: + content-length: ['360'] + content-type: [application/json] + status: {code: 200, message: OK} - request: body: null headers: {} @@ -257,48 +303,48 @@ interactions: response: body: string: !!binary | - H4sIAAAAAAAAA61UUW/aMBB+76+oIrUP04CQAGmQ0FRgUrWxrVMpVExTZOxjeCROajsIhPLfZzuG - EKRqD9tL5Lvz3Xffdxcfrq6vnQQkcvrXB3VWVoyEVJbTIrCkUnyIaULlwOve6ms33SFOmUSUiZvu - eAA7vEbsF0SU3KarlQA5cJ33ZR0GO12H5XFsPWsOq/9R2XSkCnldW7iEVh7XOjIOW5rmoo4vU4li - 5WrbWyvK/5GqYlroYk5M2UajWQ1L6Zo4FzJNgBvSR0O0Dhfh4iiZ9RMqslxCKVV5rpJs8DIHtsCk - 7uA4txMKJUXLRi1vC5NyYlszpwrCmJcAanY5I28iHMN1CJHmHJdEOJRGBVPahXMSsexLcfhhNqjU - UokrJJJmlo7IMQYgQKxiKkhAYE4zSVOmb/xGDGQVrc/F7Pf5UEbP/ucXst9+fRrDfvLoZ/dzHq5f - q/Ia/cRhNPK/PAzFvLd9QBNf5JMZmc6W38ITnLp9FLVaewNajVQHDD27OiacZwRJIBEyf57ntjsN - N2h4d9N2r++7fddvBp1O6HmLihnm8JcU3/UCLzxLkRwxgbCWKmJ5siynP/d8t+EHd42gG7ar+itE - 45xDpGCEkfackRKRc2B4r9t9fhpXaShJc6ZZtN2e/c8Uv3qxCKdEL0WtYu0RMqKcPS4apupNxTLE - JcU0Q0yqt8eIpveorurFe9Oaj/1N+P1lsYjdj9NgPWejzeITmz2edZ9lgLiIlEB66SBRP1WEzOoN - 7yfvYIeSLIYmTpMqp4R/s7bpqlDfn1fFHzIvjmluBQAA + H4sIAAAAAAAAA61UXW/aMBR9769AkdqHaUA+GlgqoYmPh06j2yTagjpNkbEv4C5xMttBMJT/Ptsx + hCBVe9heIl9f+5x7zr3x4arVclKQyLlrHdRaRQkSUkVOl8CSSvExoSmVAz+80ceuwxHOmESUietw + MoAd3iC2hpiSm2y1EiAHrvO+wmGw0zisSBK7s+Gw+h/IpiIF5IcWuKJWO67dyDlsaVaIJr/MJErU + lmdPrSj/R6lKaanBnISyn5rNelhZ18GFkFkK3Ig+BqJ7uEiXR8vsPqEiLyRUVlXr+pJNXt6BLTCp + Kzj27cRCSdm1Wavb0mSc2NLMqqYw4SWB6l3ByJsMx3STQmQFx5UQDlVQ01Rx6ZxMrOpSGr6bCaq8 + VOYKiaTppSMKjAEIEOuYShIQmNNc0ozpE6+Igayzzb6Y+T5vyvgp+Lwg++2X2QT2029BPpzzaPOr + htfsJw3jcfBwPxLz3vYeTQNRTJ/J4/Pya3SiU6ePptZjb0jrluqEkWdHx6SLnCAJJEbmz/Nd77bt + 9tuB9+hGd6F350Wdfu9D1HdfamWYw1+uBG4QRP7ZFckREwhrq2JWpMuq+/P+rdf2w6Dte2FY468Q + TQoOsaIRxtpzRcpEzoHhvS73aTapr6E0K5hW4bk9+58pfU2wGGdED0UDsfEIGVPOHhdN453bnCMu + KaY5YlK9PcY0PUdNVy/em+584n1a0MXr+mEqF8OXWb7+zZcj92yYUJ4D4iJWBumhg1T9VDEyozca + Tt/BDqV5Ah2cpbXiiv5NbFNVqb4/rso/4KyC3m4FAAA= headers: content-encoding: [gzip] - content-length: ['603'] + content-length: ['602'] content-type: [application/json] status: {code: 200, message: OK} - request: body: '{"status": "succeeded", "source_href": "/resources/CC3MHBsW6vHaL3suLVdTVbO9", - "transaction_number": "W230-378-7591", "description": "janet", "created_at": - "2014-07-28T16:30:03.302729Z", "appears_on_statement_as": "BAL*example.com", - "updated_at": "2014-07-28T16:30:03.744922Z", "order": null, "currency": "USD", + "transaction_number": "W741-253-2155", "description": "janet", "created_at": + "2014-07-31T09:51:19.303392Z", "appears_on_statement_as": "BAL*example.com", + "updated_at": "2014-07-31T09:51:19.768970Z", "order": null, "currency": "USD", "amount": 1061, "meta": {"participant_id": "2"}, "customer_href": "/customers/CU3KXdyvNSDeyLP3pAWr9hqd", - "failure_reason_code": null, "failure_reason": null, "id": "WD3k9QXZZl0ET7hWnCkZJnVP", + "failure_reason_code": null, "failure_reason": null, "id": "WD1IXiXjgMLtXAZSpgzrbB0d", "dispute": null}' headers: {} method: PUT - uri: https://api.balancedpayments.com:443/debits/WD3k9QXZZl0ET7hWnCkZJnVP + uri: https://api.balancedpayments.com:443/debits/WD1IXiXjgMLtXAZSpgzrbB0d response: body: string: !!binary | - H4sIAAAAAAAAA31SXW/aMBR9769AeZwG+RqE8NbCpGpjW6dSUjFNkbFvhUfiZP5AQyj/fXa+nDJ1 - L1bse8+595ycy81o5BDYUymcxeiHvo1Gl/rU70Iiqcy7IxTGAASI874rEhCY01LSgpmOX4iBtNWM - sqNBdlyaDSshixy46V4+hZ+fyfn09XEF5/VDWN4mPD78tvRmeqE4hrp7GX65vxPJ7HSP1qFQ6y3Z - bPff4n6c7i44qamZyrJuRyONilJJw2IK7e5VL0KVBEkgKZJmTuD5H8ZeNA7mG3+2CL2FF03i+Xwa - THdWGebwX0g4Cb0gCuIBRHLEBMLGqpSpfN94kAShNw6j+Tiaxr7lf0E0UxxSPUbU1g4VaRM5B4bP - Zt2nx5WFobxQzKjwvZnf63tNluKCdFb0LTlIpGGDH1UiLimmJWIypaQ2xvnHuQOHF1Nym/C4ySo8 - xt+fd7vM+7iJDglbHnef2PZhsGFZAuIi1SaYYEEOmh/V8bq7Xb+DPygvM5jgIreYZvyb3PVWlT5/ - GjnO69C1sZ4Mc+d2F+FemsX7ctVO7WA2OVpjkyILah+uMTayLofmYjHN/RrS5dZx6y/bXl+vu7Xp - ipHas874TgcllR7alJuf2ymBk3b6TUxb1f5VN9VfK2gqtg8EAAA= + H4sIAAAAAAAAA31Sy27bMBC85ysEHYvaEqXIrnyz40OKKm0BJ7GRohBocpMw1at8GHEN/XtJvai4 + SC+EyN2Z3RnN6cJxXAp7JoW7cH7om+OcmlO/C4mlMu+uUIQAUKDux75IQRDOKsnKwnS84AKkrWas + +GWQPZdmI0rIMgduuq/uwi87ejx83azhmHwPq+WWx8+/Lb2ZXipOoOm+Cm+uV2I7O1zjJBQquae3 + 9/tv8TBOd5ecNtSFyrJ+RyONiUpJw2IK3e71IEJVFEugKZZmTuCjy4k/n4To1o8XEVoE0XT2yUf+ + /MEqIxz+B0HxNPTDMA5GEMlxITAxVqWFyvetB9v5JZoEUTgJUBRZ/kfMMsUh1WNEY+1YkTaRcyjI + 0ax7t1lbGM5LVRgVyJ+hQd9bspSUtLdiaMlBYg0b/agKc8kIq3AhU0YbY9x/nHvm8GhKXhseb7tG + n3ds9/J0k8jd8mFTPf3h+5U/CgyuKsBcpNoEEyzIQfPjJl6rZfIBXnFeZTAlZW5VtePf5W62qvX5 + 08hx34aui/V0nDuvvwjv1C4+lOtuag+zydEa2xRZUPdwjrGR9Ti0F4tp7+eQPreu13zZ9uZ63q1N + VwVtPOuN73UwWuuhbbn9ub0SOGin38V0Ve1ffVH/BS/wjpIPBAAA headers: content-encoding: [gzip] - content-length: ['494'] + content-length: ['495'] content-type: [application/json] status: {code: 200, message: OK} version: 1 From 85f2f70f70ce79e5d2f82056251bd2b1e2dc50c2 Mon Sep 17 00:00:00 2001 From: Changaco Date: Thu, 31 Jul 2014 11:57:10 +0200 Subject: [PATCH 61/97] wrap `sync_with_balanced()` in a DB transaction --- gittip/cli.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gittip/cli.py b/gittip/cli.py index 5773166011..6aefedff65 100644 --- a/gittip/cli.py +++ b/gittip/cli.py @@ -24,7 +24,8 @@ def payday(): from gittip.billing.payday import Payday try: - sync_with_balanced(db) + with db.get_cursor() as cursor: + sync_with_balanced(cursor) Payday.start().run() except KeyboardInterrupt: pass From 177b6aafeed9402c1b8885cd22981b27f33ef72c Mon Sep 17 00:00:00 2001 From: Changaco Date: Fri, 1 Aug 2014 09:44:58 +0200 Subject: [PATCH 62/97] add missing test --- tests/py/test_billing_payday.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/py/test_billing_payday.py b/tests/py/test_billing_payday.py index 729f930c56..adbe91c674 100644 --- a/tests/py/test_billing_payday.py +++ b/tests/py/test_billing_payday.py @@ -358,6 +358,14 @@ def test_payout_no_balanced_href(self): balance=20) Payday.start().payout() + @mock.patch('gittip.billing.payday.log') + def test_payout_unreviewed(self, log): + self.make_participant('alice', claimed_time='now', is_suspicious=None, + balance=20, balanced_customer_href='foo', + last_ach_result='') + Payday.start().payout() + log.assert_any_call('UNREVIEWED: alice') + @mock.patch('gittip.billing.payday.ach_credit') def test_payout_ach_error(self, ach_credit): self.make_participant('alice', claimed_time='now', is_suspicious=False, From ea5bc5b7e3bcf10ef43f476a374a79b477ccb29e Mon Sep 17 00:00:00 2001 From: Changaco Date: Fri, 1 Aug 2014 12:22:38 +0200 Subject: [PATCH 63/97] show status of exchanges on history page --- www/%username/history/index.html.spt | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/www/%username/history/index.html.spt b/www/%username/history/index.html.spt index 0b395aa24c..024bb8ea38 100644 --- a/www/%username/history/index.html.spt +++ b/www/%username/history/index.html.spt @@ -75,14 +75,16 @@ admin_override = user.ADMIN and (participant != user.participant or 'override' i {{ -event['amount'] + event['fee'] }} {{ event['balance'] }} + {% if event['recorder'] == None %} - automatic withdrawal + automatic withdrawal + {% if event['status'] %}— status: {{ event['status'] }}{% endif %} + {% if event['note'] %}— error: “{{ event['note']|e }}”{% endif %} {% else %} - “{{ event['note']|e }}”—{{ event['recorder'] }} - {% endif %} + {% elif event['kind'] == 'charge' %} @@ -93,11 +95,17 @@ admin_override = user.ADMIN and (participant != user.participant or 'override' i {{ event['balance'] }} - {% if event['recorder'] == None %}automatic charge{% else %} + {% if event['recorder'] == None %} + automatic charge + {% else %} “{{ event['note']|e }}”—{{ event['recorder'] }} - {% endif %} + {% endif %} + {% if event['status'] in (None, 'suceeded') %} (receipt) + {% elif event['status'] in ('pending', 'failed') %} + ({{ event['status'] }}{% if event['note'] %}: “{{ event['note']|e }}”{% endif %}) + {% endif %} {% elif event['kind'] == 'transfer' %} From 4f2580608a82b01e04d8681f1292dcb5de6128ea Mon Sep 17 00:00:00 2001 From: Changaco Date: Fri, 1 Aug 2014 14:40:52 +0200 Subject: [PATCH 64/97] don't rely on the presence of an error message --- gittip/billing/exchanges.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gittip/billing/exchanges.py b/gittip/billing/exchanges.py index 143ff7a997..8a6e75c953 100644 --- a/gittip/billing/exchanges.py +++ b/gittip/billing/exchanges.py @@ -285,10 +285,10 @@ def record_exchange_result(db, exchange_id, status, error, participant): if amount < 0: amount -= fee - amount = amount if error else 0 + amount = amount if status == 'failed' else 0 propagate_exchange(cursor, participant, 'ach', error, -amount) else: - amount = amount if not error else 0 + amount = amount if status == 'succeeded' else 0 propagate_exchange(cursor, participant, 'bill', error, amount) From 51a40366546461117105991b5e9fc6a35241ee00 Mon Sep 17 00:00:00 2001 From: Changaco Date: Fri, 1 Aug 2014 14:43:12 +0200 Subject: [PATCH 65/97] failing test for `iter_payday_events()` --- gittip/testing/__init__.py | 4 ++-- tests/py/test_history.py | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/gittip/testing/__init__.py b/gittip/testing/__init__.py index 9a0e6d7630..12324ac13d 100644 --- a/gittip/testing/__init__.py +++ b/gittip/testing/__init__.py @@ -205,9 +205,9 @@ def fetch_payday(self): return self.db.one("SELECT * FROM paydays", back_as=dict) - def make_exchange(self, kind, amount, fee, participant): + def make_exchange(self, kind, amount, fee, participant, status='succeeded', error=''): e_id = record_exchange(self.db, kind, amount, fee, participant, 'pre') - record_exchange_result(self.db, e_id, 'succeeded', '', participant) + record_exchange_result(self.db, e_id, status, error, participant) return e_id diff --git a/tests/py/test_history.py b/tests/py/test_history.py index 8282d41538..f1c5a8b245 100644 --- a/tests/py/test_history.py +++ b/tests/py/test_history.py @@ -62,3 +62,21 @@ def test_iter_payday_events(self): assert carl.balance == 0 events = list(iter_payday_events(self.db, carl)) assert len(events) == 0 + + def test_iter_payday_events_with_failed_exchanges(self): + alice = self.make_participant('alice', claimed_time='now') + self.make_exchange('bill', 50, 0, alice) + self.make_exchange('bill', 12, 0, alice, status='failed') + self.make_exchange('ach', -40, 0, alice, status='failed') + events = list(iter_payday_events(self.db, alice)) + assert len(events) == 5 + assert events[0]['kind'] == 'day-open' + assert events[0]['balance'] == 50 + assert events[1]['kind'] == 'credit' + assert events[1]['balance'] == 50 + assert events[2]['kind'] == 'charge' + assert events[2]['balance'] == 50 + assert events[3]['kind'] == 'charge' + assert events[3]['balance'] == 50 + assert events[4]['kind'] == 'day-close' + assert events[4]['balance'] == '0.00' From 09c79860d0301a301760ec376976cf93f3291340 Mon Sep 17 00:00:00 2001 From: Changaco Date: Fri, 1 Aug 2014 14:43:56 +0200 Subject: [PATCH 66/97] fix `iter_payday_events()` --- gittip/utils/history.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/gittip/utils/history.py b/gittip/utils/history.py index 16e6b75bf5..7d53301851 100644 --- a/gittip/utils/history.py +++ b/gittip/utils/history.py @@ -47,10 +47,12 @@ def iter_payday_events(db, participant): if 'fee' in event: if event['amount'] > 0: kind = 'charge' - balance -= event['amount'] + if event['status'] in (None, 'succeeded'): + balance -= event['amount'] else: kind = 'credit' - balance -= event['amount'] - event['fee'] + if event['status'] != 'failed': + balance -= event['amount'] - event['fee'] else: kind = 'transfer' if event['tippee'] == username: From 4eadf87f8f8a2a4c45d99e18fd282e41b8e42999 Mon Sep 17 00:00:00 2001 From: Changaco Date: Fri, 1 Aug 2014 15:05:16 +0200 Subject: [PATCH 67/97] remove unused function --- gittip/testing/__init__.py | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/gittip/testing/__init__.py b/gittip/testing/__init__.py index 12324ac13d..269848e694 100644 --- a/gittip/testing/__init__.py +++ b/gittip/testing/__init__.py @@ -2,10 +2,8 @@ """ from __future__ import absolute_import, division, print_function, unicode_literals -import datetime import itertools import unittest -from decimal import Decimal from os.path import join, dirname, realpath from aspen import resources @@ -181,26 +179,6 @@ def make_participant(self, username, **kw): return participant - def make_payday(self, *transfers): - - with self.db.get_cursor() as cursor: - last_end = datetime.datetime(year=2012, month=1, day=1) - last_end = cursor.one("SELECT ts_end FROM paydays ORDER BY ts_end DESC LIMIT 1", default=last_end) - ts_end = last_end + datetime.timedelta(days=7) - ts_start = ts_end - datetime.timedelta(hours=1) - transfer_volume = Decimal(0) - active = set() - for i, (f, t, amount) in enumerate(transfers): - cursor.run("INSERT INTO transfers (timestamp, tipper, tippee, amount, context)" - "VALUES (%s, %s, %s, %s, 'tip')", - (ts_start + datetime.timedelta(seconds=i), f, t, amount)) - transfer_volume += Decimal(amount) - active.add(f) - active.add(t) - cursor.run("INSERT INTO paydays (ts_start, ts_end, nactive, transfer_volume) VALUES (%s, %s, %s, %s)", - (ts_start, ts_end, len(active), transfer_volume)) - - def fetch_payday(self): return self.db.one("SELECT * FROM paydays", back_as=dict) From 90e37807e175223be3ca90dd5df17d38a75c6d24 Mon Sep 17 00:00:00 2001 From: Changaco Date: Tue, 5 Aug 2014 10:58:52 +0200 Subject: [PATCH 68/97] remove unnecessary and erroneous assertion --- gittip/billing/payday.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gittip/billing/payday.py b/gittip/billing/payday.py index c49a777cc0..285708eca2 100644 --- a/gittip/billing/payday.py +++ b/gittip/billing/payday.py @@ -423,10 +423,10 @@ def settle_card_holds(self, cursor, holds): # Capture holds to bring balances back up to (at least) zero i = 0 for p in participants: - assert p.id in holds - amount = -p.new_balance - capture_card_hold(self.db, p, amount, holds.pop(p.id)) - i += 1 + if p.id in holds: + amount = -p.new_balance + capture_card_hold(self.db, p, amount, holds.pop(p.id)) + i += 1 log("Captured %i card holds." % i) # Cancel the remaining holds From f8bc65f9cc95fc404be37017d2e6c7bc9d0fb836 Mon Sep 17 00:00:00 2001 From: Changaco Date: Wed, 6 Aug 2014 13:53:00 +0200 Subject: [PATCH 69/97] dump transfers for debugging when payin fails --- gittip/billing/payday.py | 17 ++++++++++++++--- tests/py/test_billing_payday.py | 16 +++++++++++++++- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/gittip/billing/payday.py b/gittip/billing/payday.py index 285708eca2..8ae89ed2a9 100644 --- a/gittip/billing/payday.py +++ b/gittip/billing/payday.py @@ -130,9 +130,20 @@ def payin(self): holds = self.create_card_holds(cursor) self.transfer_tips(cursor) self.transfer_takes(cursor, self.ts_start) - self.settle_card_holds(cursor, holds) - self.update_balances(cursor) - check_db(cursor) + transfers = cursor.all(""" + SELECT * FROM transfers WHERE "timestamp" > %s + """, (self.ts_start,)) + try: + self.settle_card_holds(cursor, holds) + self.update_balances(cursor) + check_db(cursor) + except: + # Dump transfers for debugging + import csv + from time import time + with open('%s_transfers.csv' % time(), 'wb') as f: + csv.writer(f).writerows(transfers) + raise self.take_over_balances() diff --git a/tests/py/test_billing_payday.py b/tests/py/test_billing_payday.py index adbe91c674..016bb726b2 100644 --- a/tests/py/test_billing_payday.py +++ b/tests/py/test_billing_payday.py @@ -9,7 +9,7 @@ from gittip.billing.payday import NoPayday, Payday from gittip.exceptions import NegativeBalance from gittip.models.participant import Participant -from gittip.testing import Harness, raise_foobar +from gittip.testing import Foobar, Harness, raise_foobar from gittip.testing.balanced import BalancedHarness @@ -350,6 +350,20 @@ def test_take_over_during_payin(self): assert Participant.from_id(bruce.id).balance == 0 assert Participant.from_id(billy.id).balance == 18 + @mock.patch.object(Payday, 'fetch_card_holds') + @mock.patch('gittip.billing.payday.capture_card_hold') + def test_payin_dumps_transfers_for_debugging(self, cch, fch): + self.janet.set_tip_to(self.homer, 10) + fake_hold = mock.MagicMock() + fake_hold.amount = 1500 + fch.return_value = {self.janet.id: fake_hold} + cch.side_effect = Foobar + open = mock.MagicMock() + with mock.patch.dict(__builtins__, {'open': open}): + with self.assertRaises(Foobar): + Payday.start().payin() + assert open.call_count == 1 + class TestPayout(Harness): From 899c9cb14216c99c22d378ad827b3d42d737c8a9 Mon Sep 17 00:00:00 2001 From: Changaco Date: Wed, 6 Aug 2014 14:11:25 +0200 Subject: [PATCH 70/97] failing test for null transfers --- tests/py/test_billing_payday.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/py/test_billing_payday.py b/tests/py/test_billing_payday.py index 016bb726b2..e181fc5a78 100644 --- a/tests/py/test_billing_payday.py +++ b/tests/py/test_billing_payday.py @@ -300,6 +300,16 @@ def test_payin_doesnt_process_tips_when_goal_is_negative(self): assert Participant.from_id(alice.id).balance == 20 assert Participant.from_id(bob.id).balance == 0 + def test_payin_doesnt_make_null_transfers(self): + alice = self.make_participant('alice', claimed_time='now') + alice.set_tip_to(self.homer, 1) + alice.set_tip_to(self.homer, 0) + a_team = self.make_participant('a_team', claimed_time='now', number='plural') + a_team.add_member(alice) + Payday.start().payin() + transfers0 = self.db.all("SELECT * FROM transfers WHERE amount = 0") + assert not transfers0 + def test_transfer_takes(self): a_team = self.make_participant('a_team', claimed_time='now', number='plural', balance=20) alice = self.make_participant('alice', claimed_time='now') From 5f4c3a644997effedd92f807f7b448ddff564fc7 Mon Sep 17 00:00:00 2001 From: Changaco Date: Wed, 6 Aug 2014 14:12:26 +0200 Subject: [PATCH 71/97] don't make null transfers --- gittip/billing/payday.py | 1 + 1 file changed, 1 insertion(+) diff --git a/gittip/billing/payday.py b/gittip/billing/payday.py index 8ae89ed2a9..a842cd4f3f 100644 --- a/gittip/billing/payday.py +++ b/gittip/billing/payday.py @@ -225,6 +225,7 @@ def prepare(cursor, ts_start): CREATE OR REPLACE FUNCTION transfer(text, text, numeric, context_type) RETURNS void AS $$ BEGIN + IF ($3 = 0) THEN RETURN; END IF; UPDATE pay_participants SET new_balance = (new_balance - $3) WHERE username = $1; From f18f0ae40b5f049afe42124b41c671fe0b5bd193 Mon Sep 17 00:00:00 2001 From: Changaco Date: Wed, 6 Aug 2014 17:26:53 +0200 Subject: [PATCH 72/97] failing test for negative transfers --- tests/py/test_billing_payday.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/py/test_billing_payday.py b/tests/py/test_billing_payday.py index e181fc5a78..ae9181d72e 100644 --- a/tests/py/test_billing_payday.py +++ b/tests/py/test_billing_payday.py @@ -341,6 +341,20 @@ def test_transfer_takes(self): else: assert p.balance == 0 + @mock.patch.object(Payday, 'fetch_card_holds') + def test_transfer_takes_doesnt_make_negative_transfers(self, fch): + hold = balanced.CardHold(amount=1500, meta={'participant_id': self.janet.id}) + hold.capture = lambda *a, **kw: None + hold.save = lambda *a, **kw: None + fch.return_value = {self.janet.id: hold} + self.janet.update_number('plural') + self.janet.set_tip_to(self.homer, 10) + self.janet.add_member(self.david) + Payday.start().payin() + assert Participant.from_id(self.david.id).balance == 0 + assert Participant.from_id(self.homer.id).balance == 10 + assert Participant.from_id(self.janet.id).balance == 0 + def test_take_over_during_payin(self): alice = self.make_participant('alice', claimed_time='now', balance=50) bob = self.make_participant('bob', claimed_time='now', elsewhere='twitter') From 954ffba3ece7617a18912dd4519459b8e467379b Mon Sep 17 00:00:00 2001 From: Changaco Date: Wed, 6 Aug 2014 17:28:42 +0200 Subject: [PATCH 73/97] don't make negative transfers --- gittip/billing/payday.py | 1 + 1 file changed, 1 insertion(+) diff --git a/gittip/billing/payday.py b/gittip/billing/payday.py index a842cd4f3f..93273f3007 100644 --- a/gittip/billing/payday.py +++ b/gittip/billing/payday.py @@ -276,6 +276,7 @@ def prepare(cursor, ts_start): FROM pay_participants WHERE username = NEW.team ); + IF (team_balance <= 0) THEN RETURN NULL; END IF; actual_amount := NEW.amount; IF (team_balance < NEW.amount) THEN actual_amount := team_balance; From d00549a8514dd02663c4200c7de90f74217f12a1 Mon Sep 17 00:00:00 2001 From: Changaco Date: Wed, 6 Aug 2014 18:27:16 +0200 Subject: [PATCH 74/97] don't fetch the `Customer` when we don't actually need it --- gittip/billing/exchanges.py | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/gittip/billing/exchanges.py b/gittip/billing/exchanges.py index 8a6e75c953..a849bfed4c 100644 --- a/gittip/billing/exchanges.py +++ b/gittip/billing/exchanges.py @@ -13,6 +13,32 @@ from gittip.models.participant import Participant +# https://docs.balancedpayments.com/1.1/api/customers/ +CUSTOMER_LINKS = { + "customers.bank_accounts": "/customers/{customers.id}/bank_accounts", + "customers.card_holds": "/customers/{customers.id}/card_holds", + "customers.cards": "/customers/{customers.id}/cards", + "customers.credits": "/customers/{customers.id}/credits", + "customers.debits": "/customers/{customers.id}/debits", + "customers.destination": "/resources/{customers.destination}", + "customers.disputes": "/customers/{customers.id}/disputes", + "customers.external_accounts": "/customers/{customers.id}/external_accounts", + "customers.orders": "/customers/{customers.id}/orders", + "customers.refunds": "/customers/{customers.id}/refunds", + "customers.reversals": "/customers/{customers.id}/reversals", + "customers.source": "/resources/{customers.source}", + "customers.transactions": "/customers/{customers.id}/transactions" +} + + +def customer_from_href(href): + """This functions "manually" builds a minimal Customer instance. + """ + id = href.rsplit('/', 1)[1] + d = {'href': href, 'id': id, 'links': {}, 'meta': {}} + return balanced.Customer(customers=[d], links=CUSTOMER_LINKS) + + # Balanced has a $0.50 minimum. We go even higher to avoid onerous # per-transaction fees. See: # https://github.com/gittip/www.gittip.com/issues/167 @@ -109,7 +135,7 @@ def ach_credit(db, participant, withhold, minimum_credit=MINIMUM_CREDIT): e_id = record_exchange(db, 'ach', -credit_amount, fee, participant, 'pre') meta = dict(exchange_id=e_id, participant_id=participant.id) try: - customer = balanced.Customer.fetch(balanced_customer_href) + customer = customer_from_href(balanced_customer_href) ba = customer.bank_accounts.one() ba.credit(amount=cents, description=participant.username, meta=meta) record_exchange_result(db, e_id, 'pending', None, participant) @@ -158,7 +184,7 @@ def create_card_hold(db, participant, amount): hold = None try: - card = balanced.Customer.fetch(balanced_customer_href).cards.one() + card = customer_from_href(balanced_customer_href).cards.one() hold = card.hold( amount=cents , description=username , meta=dict(participant_id=participant.id, state='new') From 2b6ecc0b89f38e4860558d7664f8da0db8e1254c Mon Sep 17 00:00:00 2001 From: Changaco Date: Wed, 6 Aug 2014 18:29:26 +0200 Subject: [PATCH 75/97] update tests --- gittip/testing/__init__.py | 4 ---- tests/py/test_billing_exchanges.py | 10 +++++----- tests/py/test_billing_payday.py | 4 ++-- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/gittip/testing/__init__.py b/gittip/testing/__init__.py index 269848e694..502281b9e8 100644 --- a/gittip/testing/__init__.py +++ b/gittip/testing/__init__.py @@ -190,7 +190,3 @@ def make_exchange(self, kind, amount, fee, participant, status='succeeded', erro class Foobar(Exception): pass - - -def raise_foobar(*a): - raise Foobar diff --git a/tests/py/test_billing_exchanges.py b/tests/py/test_billing_exchanges.py index 0ba21c729f..87648a3536 100644 --- a/tests/py/test_billing_exchanges.py +++ b/tests/py/test_billing_exchanges.py @@ -21,7 +21,7 @@ ) from gittip.exceptions import NegativeBalance, NoBalancedCustomerHref, NotWhitelisted from gittip.models.participant import Participant -from gittip.testing import Foobar, Harness, raise_foobar +from gittip.testing import Foobar, Harness from gittip.testing.balanced import BalancedHarness @@ -46,7 +46,7 @@ def test_ach_credit_amount_under_minimum(self): @mock.patch('balanced.Customer') def test_ach_credit_failure(self, Customer): - Customer.fetch = raise_foobar + Customer.side_effect = Foobar bob = self.make_participant('bob', last_ach_result="failure", balance=20, balanced_customer_href=self.homer_href, is_suspicious=False) @@ -72,7 +72,7 @@ def test_create_card_hold_for_suspicious_raises_NotWhitelisted(self): @mock.patch('balanced.Customer') def test_create_card_hold_failure(self, Customer): - Customer.fetch = raise_foobar + Customer.side_effect = Foobar hold, error = create_card_hold(self.db, self.janet, D('1.00')) assert hold is None assert error == "Foobar()" @@ -360,8 +360,8 @@ def test_sync_with_balanced_deletes_charges_that_didnt_happen(self): def test_sync_with_balanced_reverts_credits_that_didnt_happen(self): self.make_exchange('bill', 41, 0, self.homer) with mock.patch('gittip.billing.exchanges.record_exchange_result') as rer \ - , mock.patch('balanced.Customer.fetch') as fetch: - rer.side_effect = fetch.side_effect = Foobar + , mock.patch('balanced.Customer') as Customer: + rer.side_effect = Customer.side_effect = Foobar with self.assertRaises(Foobar): ach_credit(self.db, self.homer, 0, 0) exchange = self.db.one("SELECT * FROM exchanges WHERE amount < 0") diff --git a/tests/py/test_billing_payday.py b/tests/py/test_billing_payday.py index ae9181d72e..a166ca699b 100644 --- a/tests/py/test_billing_payday.py +++ b/tests/py/test_billing_payday.py @@ -9,7 +9,7 @@ from gittip.billing.payday import NoPayday, Payday from gittip.exceptions import NegativeBalance from gittip.models.participant import Participant -from gittip.testing import Foobar, Harness, raise_foobar +from gittip.testing import Foobar, Harness from gittip.testing.balanced import BalancedHarness @@ -281,7 +281,7 @@ def test_payin_cant_make_balances_more_negative(self): @mock.patch('balanced.Customer') def test_card_hold_error(self, Customer, fch): self.janet.set_tip_to(self.homer, 17) - Customer.fetch = raise_foobar + Customer.side_effect = Foobar fch.return_value = {} Payday.start().payin() payday = self.fetch_payday() From 5f142f0a7d3ed8dba0ee8c49bf911dca3e3c55f7 Mon Sep 17 00:00:00 2001 From: Changaco Date: Wed, 6 Aug 2014 18:41:32 +0200 Subject: [PATCH 76/97] update test fixtures --- tests/py/fixtures/TestCardHolds.yml | 610 ++++++++------------- tests/py/fixtures/TestClosing.yml | 112 ++-- tests/py/fixtures/TestCredits.yml | 38 +- tests/py/fixtures/TestPayin.yml | 361 ++++++------ tests/py/fixtures/TestSyncWithBalanced.yml | 172 +++--- 5 files changed, 546 insertions(+), 747 deletions(-) diff --git a/tests/py/fixtures/TestCardHolds.yml b/tests/py/fixtures/TestCardHolds.yml index 6fcf2aedea..e73092f836 100644 --- a/tests/py/fixtures/TestCardHolds.yml +++ b/tests/py/fixtures/TestCardHolds.yml @@ -1,26 +1,4 @@ interactions: -- request: - body: null - headers: {} - method: GET - uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd - response: - body: - string: !!binary | - H4sIAAAAAAAAA41Uy27bMBC85ysEnWvLipQ29i1oby2CAG3RokUh0CQLEZFIhY+ggqF/79KWZJpU - wlx02J0ZcmdHPFwlSYqN0qKlUqW75DcUkuRw/EKLo5ZClZumeTfVGsYfLXQCAUwJI7EPhDqhSjOO - NBN8VBlFhlnNdARpSiqkAZFeb/Jytfmw2uTf8ptdWezKcp0Xxfvb7a90pmBJX6UU622+uSlKh0LE - vmoF17U/DCP22I/fi88/Sf98//UT7b88FN3dD7mtn8j5zK4W3B8wrSX9a+nZ7GAWVwKjcY24rpRG - 2lgjUy5WLdK4Ph/XUo08i+mTheZp4KCdradI+qMhQiRV3qYw070HhD3BSun1cjkPy/bivhcg0glo - NBUWZKGJheFa9m43GGRvFNxDqWopdUrxqkFKl/6YtEWsCYrsInADnPXH5sdO6oT3HP31HOE0A9eO - eVbZYV7s2B/GFTlEjCSpatGQ4yqdKDhkRobMwZ2S7GjsEX+sED6aFJG5hAZKhO5ZTGLELHDdv/UF - H5xfesEM+k9TySEFbxsmhAeXsrZFLDlBAiZhqjOaRsgzKuBribhC2D5eEY0LZKADj4ThsRkm0AL7 - GV5m1ESuIOkECxSEJKe3/ZVwjpiAC08tieZpAsH/NVwN/wHQiK4AUwYAAA== - headers: - content-encoding: [gzip] - content-length: ['499'] - content-type: [application/json] - status: {code: 200, message: OK} - request: body: null headers: {} @@ -58,13 +36,13 @@ interactions: body: {string: !!python/unicode "{\n \"card_holds\": [\n {\n \"status\": \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"card\": \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \"debit\": null\n },\n \"updated_at\": - \"2014-07-28T12:44:02.482667Z\",\n \"created_at\": \"2014-07-28T12:44:02.246126Z\",\n - \ \"transaction_number\": \"HL223-221-7905\",\n \"expires_at\": \"2014-08-04T12:44:02.393923Z\",\n + \"2014-08-06T16:10:31.247252Z\",\n \"created_at\": \"2014-08-06T16:10:30.903504Z\",\n + \ \"transaction_number\": \"HL645-330-0776\",\n \"expires_at\": \"2014-08-13T16:10:31.135263Z\",\n \ \"failure_reason\": null,\n \"currency\": \"USD\",\n \"amount\": 2091,\n \"meta\": {\n \"state\": \"new\",\n \"participant_id\": - \"2\"\n },\n \"href\": \"/card_holds/HL6DNvcbs8HPF3Hk2S7ktMY5\",\n + \"2\"\n },\n \"href\": \"/card_holds/HL1sTnLxawUR168Ucdth56zH\",\n \ \"failure_reason_code\": null,\n \"voided_at\": null,\n \"id\": - \"HL6DNvcbs8HPF3Hk2S7ktMY5\"\n }\n ],\n \"links\": {\n \"card_holds.events\": + \"HL1sTnLxawUR168Ucdth56zH\"\n }\n ],\n \"links\": {\n \"card_holds.events\": \"/card_holds/{card_holds.id}/events\",\n \"card_holds.card\": \"/cards/{card_holds.card}\",\n \ \"card_holds.debits\": \"/card_holds/{card_holds.id}/debits\",\n \"card_holds.debit\": \"/debits/{card_holds.debit}\"\n }\n}"} @@ -73,51 +51,52 @@ interactions: content-type: [application/json] status: {code: 201, message: CREATED} - request: - body: '{"amount": 1000, "description": "janet"}' + body: '{"amount": 1000, "meta": {"exchange_id": 1, "participant_id": 2}, "description": + "janet"}' headers: {} method: POST - uri: https://api.balancedpayments.com:443/card_holds/HL6DNvcbs8HPF3Hk2S7ktMY5/debits + uri: https://api.balancedpayments.com:443/card_holds/HL1sTnLxawUR168Ucdth56zH/debits response: body: {string: !!python/unicode "{\n \"debits\": [\n {\n \"status\": \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"customer\": \"CU3KXdyvNSDeyLP3pAWr9hqd\",\n \"source\": \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \ \"order\": null,\n \"dispute\": null\n },\n \"updated_at\": - \"2014-07-28T12:44:03.374049Z\",\n \"created_at\": \"2014-07-28T12:44:02.942161Z\",\n - \ \"transaction_number\": \"W481-816-9765\",\n \"failure_reason\": + \"2014-08-06T16:10:32.094369Z\",\n \"created_at\": \"2014-08-06T16:10:31.653991Z\",\n + \ \"transaction_number\": \"W530-573-0663\",\n \"failure_reason\": null,\n \"currency\": \"USD\",\n \"amount\": 1000,\n \"failure_reason_code\": - null,\n \"meta\": {\n \"state\": \"new\",\n \"participant_id\": - \"2\"\n },\n \"href\": \"/debits/WD6EAiSkHLrwka8kNBgz40yd\",\n \"appears_on_statement_as\": - \"BAL*example.com\",\n \"id\": \"WD6EAiSkHLrwka8kNBgz40yd\"\n }\n + null,\n \"meta\": {\n \"exchange_id\": \"1\",\n \"participant_id\": + \"2\"\n },\n \"href\": \"/debits/WD1tJAtTmE3J2iFFK32qNVjL\",\n \"appears_on_statement_as\": + \"BAL*example.com\",\n \"id\": \"WD1tJAtTmE3J2iFFK32qNVjL\"\n }\n \ ],\n \"links\": {\n \"debits.customer\": \"/customers/{debits.customer}\",\n \ \"debits.dispute\": \"/disputes/{debits.dispute}\",\n \"debits.source\": \"/resources/{debits.source}\",\n \"debits.order\": \"/orders/{debits.order}\",\n \ \"debits.refunds\": \"/debits/{debits.id}/refunds\",\n \"debits.events\": \"/debits/{debits.id}/events\"\n }\n}"} headers: - content-length: ['1063'] + content-length: ['1067'] content-type: [application/json] status: {code: 201, message: CREATED} - request: body: '{"status": "succeeded", "description": "janet", "amount": 2091, "created_at": - "2014-07-28T12:44:02.246126Z", "card_href": "/cards/CC3MHBsW6vHaL3suLVdTVbO9", - "updated_at": "2014-07-28T12:44:02.482667Z", "expires_at": "2014-08-04T12:44:02.393923Z", - "failure_reason": null, "currency": "USD", "transaction_number": "HL223-221-7905", + "2014-08-06T16:10:30.903504Z", "card_href": "/cards/CC3MHBsW6vHaL3suLVdTVbO9", + "updated_at": "2014-08-06T16:10:31.247252Z", "expires_at": "2014-08-13T16:10:31.135263Z", + "failure_reason": null, "currency": "USD", "transaction_number": "HL645-330-0776", "meta": {"state": "captured", "participant_id": "2"}, "debit": null, "failure_reason_code": - null, "voided_at": null, "id": "HL6DNvcbs8HPF3Hk2S7ktMY5"}' + null, "voided_at": null, "id": "HL1sTnLxawUR168Ucdth56zH"}' headers: {} method: PUT - uri: https://api.balancedpayments.com:443/card_holds/HL6DNvcbs8HPF3Hk2S7ktMY5 + uri: https://api.balancedpayments.com:443/card_holds/HL1sTnLxawUR168Ucdth56zH response: body: {string: !!python/unicode "{\n \"card_holds\": [\n {\n \"status\": \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"card\": - \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \"debit\": \"WD6EAiSkHLrwka8kNBgz40yd\"\n - \ },\n \"updated_at\": \"2014-07-28T12:44:03.779589Z\",\n \"created_at\": - \"2014-07-28T12:44:02.246126Z\",\n \"transaction_number\": \"HL223-221-7905\",\n - \ \"expires_at\": \"2014-08-04T12:44:02.393923Z\",\n \"failure_reason\": + \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \"debit\": \"WD1tJAtTmE3J2iFFK32qNVjL\"\n + \ },\n \"updated_at\": \"2014-08-06T16:10:32.534385Z\",\n \"created_at\": + \"2014-08-06T16:10:30.903504Z\",\n \"transaction_number\": \"HL645-330-0776\",\n + \ \"expires_at\": \"2014-08-13T16:10:31.135263Z\",\n \"failure_reason\": null,\n \"currency\": \"USD\",\n \"amount\": 2091,\n \"meta\": {\n \"state\": \"captured\",\n \"participant_id\": \"2\"\n },\n - \ \"href\": \"/card_holds/HL6DNvcbs8HPF3Hk2S7ktMY5\",\n \"failure_reason_code\": - null,\n \"voided_at\": null,\n \"id\": \"HL6DNvcbs8HPF3Hk2S7ktMY5\"\n + \ \"href\": \"/card_holds/HL1sTnLxawUR168Ucdth56zH\",\n \"failure_reason_code\": + null,\n \"voided_at\": null,\n \"id\": \"HL1sTnLxawUR168Ucdth56zH\"\n \ }\n ],\n \"links\": {\n \"card_holds.events\": \"/card_holds/{card_holds.id}/events\",\n \ \"card_holds.card\": \"/cards/{card_holds.card}\",\n \"card_holds.debits\": \"/card_holds/{card_holds.id}/debits\",\n \"card_holds.debit\": \"/debits/{card_holds.debit}\"\n @@ -126,28 +105,6 @@ interactions: content-length: ['971'] content-type: [application/json] status: {code: 200, message: OK} -- request: - body: null - headers: {} - method: GET - uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd - response: - body: - string: !!binary | - H4sIAAAAAAAAA41Uy27bMBC85ysEnWvLipQ29i1oby2CAG3RokUh0CQLEZFIhY+ggqF/79KWZJpU - wlx02J0ZcmdHPFwlSYqN0qKlUqW75DcUkuRw/EKLo5ZClZumeTfVGsYfLXQCAUwJI7EPhDqhSjOO - NBN8VBlFhlnNdARpSiqkAZFeb/Jytfmw2uTf8ptdWezKcp0Xxfvb7a90pmBJX6UU622+uSlKh0LE - vmoF17U/DCP22I/fi88/Sf98//UT7b88FN3dD7mtn8j5zK4W3B8wrSX9a+nZ7GAWVwKjcY24rpRG - 2lgjUy5WLdK4Ph/XUo08i+mTheZp4KCdradI+qMhQiRV3qYw070HhD3BSun1cjkPy/bivhcg0glo - NBUWZKGJheFa9m43GGRvFNxDqWopdUrxqkFKl/6YtEWsCYrsInADnPXH5sdO6oT3HP31HOE0A9eO - eVbZYV7s2B/GFTlEjCSpatGQ4yqdKDhkRobMwZ2S7GjsEX+sED6aFJG5hAZKhO5ZTGLELHDdv/UF - H5xfesEM+k9TySEFbxsmhAeXsrZFLDlBAiZhqjOaRsgzKuBribhC2D5eEY0LZKADj4ThsRkm0AL7 - GV5m1ESuIOkECxSEJKe3/ZVwjpiAC08tieZpAsH/NVwN/wHQiK4AUwYAAA== - headers: - content-encoding: [gzip] - content-length: ['499'] - content-type: [application/json] - status: {code: 200, message: OK} - request: body: null headers: {} @@ -185,13 +142,13 @@ interactions: body: {string: !!python/unicode "{\n \"card_holds\": [\n {\n \"status\": \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"card\": \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \"debit\": null\n },\n \"updated_at\": - \"2014-07-28T12:44:05.151262Z\",\n \"created_at\": \"2014-07-28T12:44:04.938351Z\",\n - \ \"transaction_number\": \"HL278-301-3242\",\n \"expires_at\": \"2014-08-04T12:44:05.064676Z\",\n + \"2014-08-06T16:10:33.479111Z\",\n \"created_at\": \"2014-08-06T16:10:33.257372Z\",\n + \ \"transaction_number\": \"HL744-952-5009\",\n \"expires_at\": \"2014-08-13T16:10:33.392359Z\",\n \ \"failure_reason\": null,\n \"currency\": \"USD\",\n \"amount\": 2091,\n \"meta\": {\n \"state\": \"new\",\n \"participant_id\": - \"2\"\n },\n \"href\": \"/card_holds/HL6GP82LFspeIjW9XWztLEwE\",\n + \"2\"\n },\n \"href\": \"/card_holds/HL1vxwGqvZFvkpjdSyBB74SF\",\n \ \"failure_reason_code\": null,\n \"voided_at\": null,\n \"id\": - \"HL6GP82LFspeIjW9XWztLEwE\"\n }\n ],\n \"links\": {\n \"card_holds.events\": + \"HL1vxwGqvZFvkpjdSyBB74SF\"\n }\n ],\n \"links\": {\n \"card_holds.events\": \"/card_holds/{card_holds.id}/events\",\n \"card_holds.card\": \"/cards/{card_holds.card}\",\n \ \"card_holds.debits\": \"/card_holds/{card_holds.id}/debits\",\n \"card_holds.debit\": \"/debits/{card_holds.debit}\"\n }\n}"} @@ -200,51 +157,52 @@ interactions: content-type: [application/json] status: {code: 201, message: CREATED} - request: - body: '{"amount": 2091, "description": "janet"}' + body: '{"amount": 2091, "meta": {"exchange_id": 2, "participant_id": 2}, "description": + "janet"}' headers: {} method: POST - uri: https://api.balancedpayments.com:443/card_holds/HL6GP82LFspeIjW9XWztLEwE/debits + uri: https://api.balancedpayments.com:443/card_holds/HL1vxwGqvZFvkpjdSyBB74SF/debits response: body: {string: !!python/unicode "{\n \"debits\": [\n {\n \"status\": \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"customer\": \"CU3KXdyvNSDeyLP3pAWr9hqd\",\n \"source\": \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \ \"order\": null,\n \"dispute\": null\n },\n \"updated_at\": - \"2014-07-28T12:44:06.139206Z\",\n \"created_at\": \"2014-07-28T12:44:05.676736Z\",\n - \ \"transaction_number\": \"W013-682-4382\",\n \"failure_reason\": + \"2014-08-06T16:10:34.625458Z\",\n \"created_at\": \"2014-08-06T16:10:34.219263Z\",\n + \ \"transaction_number\": \"W452-602-8336\",\n \"failure_reason\": null,\n \"currency\": \"USD\",\n \"amount\": 2091,\n \"failure_reason_code\": - null,\n \"meta\": {\n \"state\": \"new\",\n \"participant_id\": - \"2\"\n },\n \"href\": \"/debits/WD6HEBOYVE3p11E9idopswsd\",\n \"appears_on_statement_as\": - \"BAL*example.com\",\n \"id\": \"WD6HEBOYVE3p11E9idopswsd\"\n }\n + null,\n \"meta\": {\n \"exchange_id\": \"2\",\n \"participant_id\": + \"2\"\n },\n \"href\": \"/debits/WD1wCGE8xvdpujzu33oQ0Iuc\",\n \"appears_on_statement_as\": + \"BAL*example.com\",\n \"id\": \"WD1wCGE8xvdpujzu33oQ0Iuc\"\n }\n \ ],\n \"links\": {\n \"debits.customer\": \"/customers/{debits.customer}\",\n \ \"debits.dispute\": \"/disputes/{debits.dispute}\",\n \"debits.source\": \"/resources/{debits.source}\",\n \"debits.order\": \"/orders/{debits.order}\",\n \ \"debits.refunds\": \"/debits/{debits.id}/refunds\",\n \"debits.events\": \"/debits/{debits.id}/events\"\n }\n}"} headers: - content-length: ['1063'] + content-length: ['1067'] content-type: [application/json] status: {code: 201, message: CREATED} - request: body: '{"status": "succeeded", "description": "janet", "amount": 2091, "created_at": - "2014-07-28T12:44:04.938351Z", "card_href": "/cards/CC3MHBsW6vHaL3suLVdTVbO9", - "updated_at": "2014-07-28T12:44:05.151262Z", "expires_at": "2014-08-04T12:44:05.064676Z", - "failure_reason": null, "currency": "USD", "transaction_number": "HL278-301-3242", + "2014-08-06T16:10:33.257372Z", "card_href": "/cards/CC3MHBsW6vHaL3suLVdTVbO9", + "updated_at": "2014-08-06T16:10:33.479111Z", "expires_at": "2014-08-13T16:10:33.392359Z", + "failure_reason": null, "currency": "USD", "transaction_number": "HL744-952-5009", "meta": {"state": "captured", "participant_id": "2"}, "debit": null, "failure_reason_code": - null, "voided_at": null, "id": "HL6GP82LFspeIjW9XWztLEwE"}' + null, "voided_at": null, "id": "HL1vxwGqvZFvkpjdSyBB74SF"}' headers: {} method: PUT - uri: https://api.balancedpayments.com:443/card_holds/HL6GP82LFspeIjW9XWztLEwE + uri: https://api.balancedpayments.com:443/card_holds/HL1vxwGqvZFvkpjdSyBB74SF response: body: {string: !!python/unicode "{\n \"card_holds\": [\n {\n \"status\": \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"card\": - \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \"debit\": \"WD6HEBOYVE3p11E9idopswsd\"\n - \ },\n \"updated_at\": \"2014-07-28T12:44:06.588345Z\",\n \"created_at\": - \"2014-07-28T12:44:04.938351Z\",\n \"transaction_number\": \"HL278-301-3242\",\n - \ \"expires_at\": \"2014-08-04T12:44:05.064676Z\",\n \"failure_reason\": + \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \"debit\": \"WD1wCGE8xvdpujzu33oQ0Iuc\"\n + \ },\n \"updated_at\": \"2014-08-06T16:10:35.009812Z\",\n \"created_at\": + \"2014-08-06T16:10:33.257372Z\",\n \"transaction_number\": \"HL744-952-5009\",\n + \ \"expires_at\": \"2014-08-13T16:10:33.392359Z\",\n \"failure_reason\": null,\n \"currency\": \"USD\",\n \"amount\": 2091,\n \"meta\": {\n \"state\": \"captured\",\n \"participant_id\": \"2\"\n },\n - \ \"href\": \"/card_holds/HL6GP82LFspeIjW9XWztLEwE\",\n \"failure_reason_code\": - null,\n \"voided_at\": null,\n \"id\": \"HL6GP82LFspeIjW9XWztLEwE\"\n + \ \"href\": \"/card_holds/HL1vxwGqvZFvkpjdSyBB74SF\",\n \"failure_reason_code\": + null,\n \"voided_at\": null,\n \"id\": \"HL1vxwGqvZFvkpjdSyBB74SF\"\n \ }\n ],\n \"links\": {\n \"card_holds.events\": \"/card_holds/{card_holds.id}/events\",\n \ \"card_holds.card\": \"/cards/{card_holds.card}\",\n \"card_holds.debits\": \"/card_holds/{card_holds.id}/debits\",\n \"card_holds.debit\": \"/debits/{card_holds.debit}\"\n @@ -253,28 +211,6 @@ interactions: content-length: ['971'] content-type: [application/json] status: {code: 200, message: OK} -- request: - body: null - headers: {} - method: GET - uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd - response: - body: - string: !!binary | - H4sIAAAAAAAAA41Uy27bMBC85ysEnWvLipQ29i1oby2CAG3RokUh0CQLEZFIhY+ggqF/79KWZJpU - wlx02J0ZcmdHPFwlSYqN0qKlUqW75DcUkuRw/EKLo5ZClZumeTfVGsYfLXQCAUwJI7EPhDqhSjOO - NBN8VBlFhlnNdARpSiqkAZFeb/Jytfmw2uTf8ptdWezKcp0Xxfvb7a90pmBJX6UU622+uSlKh0LE - vmoF17U/DCP22I/fi88/Sf98//UT7b88FN3dD7mtn8j5zK4W3B8wrSX9a+nZ7GAWVwKjcY24rpRG - 2lgjUy5WLdK4Ph/XUo08i+mTheZp4KCdradI+qMhQiRV3qYw070HhD3BSun1cjkPy/bivhcg0glo - NBUWZKGJheFa9m43GGRvFNxDqWopdUrxqkFKl/6YtEWsCYrsInADnPXH5sdO6oT3HP31HOE0A9eO - eVbZYV7s2B/GFTlEjCSpatGQ4yqdKDhkRobMwZ2S7GjsEX+sED6aFJG5hAZKhO5ZTGLELHDdv/UF - H5xfesEM+k9TySEFbxsmhAeXsrZFLDlBAiZhqjOaRsgzKuBribhC2D5eEY0LZKADj4ThsRkm0AL7 - GV5m1ESuIOkECxSEJKe3/ZVwjpiAC08tieZpAsH/NVwN/wHQiK4AUwYAAA== - headers: - content-encoding: [gzip] - content-length: ['499'] - content-type: [application/json] - status: {code: 200, message: OK} - request: body: null headers: {} @@ -312,13 +248,13 @@ interactions: body: {string: !!python/unicode "{\n \"card_holds\": [\n {\n \"status\": \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"card\": \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \"debit\": null\n },\n \"updated_at\": - \"2014-07-28T12:44:08.073612Z\",\n \"created_at\": \"2014-07-28T12:44:07.849448Z\",\n - \ \"transaction_number\": \"HL787-086-5782\",\n \"expires_at\": \"2014-08-04T12:44:07.976594Z\",\n + \"2014-08-06T16:10:36.017293Z\",\n \"created_at\": \"2014-08-06T16:10:35.807447Z\",\n + \ \"transaction_number\": \"HL893-713-2609\",\n \"expires_at\": \"2014-08-13T16:10:35.934417Z\",\n \ \"failure_reason\": null,\n \"currency\": \"USD\",\n \"amount\": 2091,\n \"meta\": {\n \"state\": \"new\",\n \"participant_id\": - \"2\"\n },\n \"href\": \"/card_holds/HL6K6eJyyZDc7Wa2YNTWgl1P\",\n + \"2\"\n },\n \"href\": \"/card_holds/HL1yplLIcgwLVUWkXXK5D96l\",\n \ \"failure_reason_code\": null,\n \"voided_at\": null,\n \"id\": - \"HL6K6eJyyZDc7Wa2YNTWgl1P\"\n }\n ],\n \"links\": {\n \"card_holds.events\": + \"HL1yplLIcgwLVUWkXXK5D96l\"\n }\n ],\n \"links\": {\n \"card_holds.events\": \"/card_holds/{card_holds.id}/events\",\n \"card_holds.card\": \"/cards/{card_holds.card}\",\n \ \"card_holds.debits\": \"/card_holds/{card_holds.id}/debits\",\n \"card_holds.debit\": \"/debits/{card_holds.debit}\"\n }\n}"} @@ -327,51 +263,52 @@ interactions: content-type: [application/json] status: {code: 201, message: CREATED} - request: - body: '{"amount": 1576, "description": "janet"}' + body: '{"amount": 1576, "meta": {"exchange_id": 3, "participant_id": 2}, "description": + "janet"}' headers: {} method: POST - uri: https://api.balancedpayments.com:443/card_holds/HL6K6eJyyZDc7Wa2YNTWgl1P/debits + uri: https://api.balancedpayments.com:443/card_holds/HL1yplLIcgwLVUWkXXK5D96l/debits response: body: {string: !!python/unicode "{\n \"debits\": [\n {\n \"status\": \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"customer\": \"CU3KXdyvNSDeyLP3pAWr9hqd\",\n \"source\": \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \ \"order\": null,\n \"dispute\": null\n },\n \"updated_at\": - \"2014-07-28T12:44:08.888162Z\",\n \"created_at\": \"2014-07-28T12:44:08.477253Z\",\n - \ \"transaction_number\": \"W346-884-7108\",\n \"failure_reason\": + \"2014-08-06T16:10:37.174940Z\",\n \"created_at\": \"2014-08-06T16:10:36.767099Z\",\n + \ \"transaction_number\": \"W550-194-1344\",\n \"failure_reason\": null,\n \"currency\": \"USD\",\n \"amount\": 1576,\n \"failure_reason_code\": - null,\n \"meta\": {\n \"state\": \"new\",\n \"participant_id\": - \"2\"\n },\n \"href\": \"/debits/WD6KNTVQcbvgalfEe3OD7CRv\",\n \"appears_on_statement_as\": - \"BAL*example.com\",\n \"id\": \"WD6KNTVQcbvgalfEe3OD7CRv\"\n }\n + null,\n \"meta\": {\n \"exchange_id\": \"3\",\n \"participant_id\": + \"2\"\n },\n \"href\": \"/debits/WD1zuksSljYxGxbMMHmaFpw4\",\n \"appears_on_statement_as\": + \"BAL*example.com\",\n \"id\": \"WD1zuksSljYxGxbMMHmaFpw4\"\n }\n \ ],\n \"links\": {\n \"debits.customer\": \"/customers/{debits.customer}\",\n \ \"debits.dispute\": \"/disputes/{debits.dispute}\",\n \"debits.source\": \"/resources/{debits.source}\",\n \"debits.order\": \"/orders/{debits.order}\",\n \ \"debits.refunds\": \"/debits/{debits.id}/refunds\",\n \"debits.events\": \"/debits/{debits.id}/events\"\n }\n}"} headers: - content-length: ['1063'] + content-length: ['1067'] content-type: [application/json] status: {code: 201, message: CREATED} - request: body: '{"status": "succeeded", "description": "janet", "amount": 2091, "created_at": - "2014-07-28T12:44:07.849448Z", "card_href": "/cards/CC3MHBsW6vHaL3suLVdTVbO9", - "updated_at": "2014-07-28T12:44:08.073612Z", "expires_at": "2014-08-04T12:44:07.976594Z", - "failure_reason": null, "currency": "USD", "transaction_number": "HL787-086-5782", + "2014-08-06T16:10:35.807447Z", "card_href": "/cards/CC3MHBsW6vHaL3suLVdTVbO9", + "updated_at": "2014-08-06T16:10:36.017293Z", "expires_at": "2014-08-13T16:10:35.934417Z", + "failure_reason": null, "currency": "USD", "transaction_number": "HL893-713-2609", "meta": {"state": "captured", "participant_id": "2"}, "debit": null, "failure_reason_code": - null, "voided_at": null, "id": "HL6K6eJyyZDc7Wa2YNTWgl1P"}' + null, "voided_at": null, "id": "HL1yplLIcgwLVUWkXXK5D96l"}' headers: {} method: PUT - uri: https://api.balancedpayments.com:443/card_holds/HL6K6eJyyZDc7Wa2YNTWgl1P + uri: https://api.balancedpayments.com:443/card_holds/HL1yplLIcgwLVUWkXXK5D96l response: body: {string: !!python/unicode "{\n \"card_holds\": [\n {\n \"status\": \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"card\": - \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \"debit\": \"WD6KNTVQcbvgalfEe3OD7CRv\"\n - \ },\n \"updated_at\": \"2014-07-28T12:44:09.290268Z\",\n \"created_at\": - \"2014-07-28T12:44:07.849448Z\",\n \"transaction_number\": \"HL787-086-5782\",\n - \ \"expires_at\": \"2014-08-04T12:44:07.976594Z\",\n \"failure_reason\": + \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \"debit\": \"WD1zuksSljYxGxbMMHmaFpw4\"\n + \ },\n \"updated_at\": \"2014-08-06T16:10:37.710099Z\",\n \"created_at\": + \"2014-08-06T16:10:35.807447Z\",\n \"transaction_number\": \"HL893-713-2609\",\n + \ \"expires_at\": \"2014-08-13T16:10:35.934417Z\",\n \"failure_reason\": null,\n \"currency\": \"USD\",\n \"amount\": 2091,\n \"meta\": {\n \"state\": \"captured\",\n \"participant_id\": \"2\"\n },\n - \ \"href\": \"/card_holds/HL6K6eJyyZDc7Wa2YNTWgl1P\",\n \"failure_reason_code\": - null,\n \"voided_at\": null,\n \"id\": \"HL6K6eJyyZDc7Wa2YNTWgl1P\"\n + \ \"href\": \"/card_holds/HL1yplLIcgwLVUWkXXK5D96l\",\n \"failure_reason_code\": + null,\n \"voided_at\": null,\n \"id\": \"HL1yplLIcgwLVUWkXXK5D96l\"\n \ }\n ],\n \"links\": {\n \"card_holds.events\": \"/card_holds/{card_holds.id}/events\",\n \ \"card_holds.card\": \"/cards/{card_holds.card}\",\n \"card_holds.debits\": \"/card_holds/{card_holds.id}/debits\",\n \"card_holds.debit\": \"/debits/{card_holds.debit}\"\n @@ -380,28 +317,6 @@ interactions: content-length: ['971'] content-type: [application/json] status: {code: 200, message: OK} -- request: - body: null - headers: {} - method: GET - uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd - response: - body: - string: !!binary | - H4sIAAAAAAAAA41Uy27bMBC85ysEnWvLipQ29i1oby2CAG3RokUh0CQLEZFIhY+ggqF/79KWZJpU - wlx02J0ZcmdHPFwlSYqN0qKlUqW75DcUkuRw/EKLo5ZClZumeTfVGsYfLXQCAUwJI7EPhDqhSjOO - NBN8VBlFhlnNdARpSiqkAZFeb/Jytfmw2uTf8ptdWezKcp0Xxfvb7a90pmBJX6UU622+uSlKh0LE - vmoF17U/DCP22I/fi88/Sf98//UT7b88FN3dD7mtn8j5zK4W3B8wrSX9a+nZ7GAWVwKjcY24rpRG - 2lgjUy5WLdK4Ph/XUo08i+mTheZp4KCdradI+qMhQiRV3qYw070HhD3BSun1cjkPy/bivhcg0glo - NBUWZKGJheFa9m43GGRvFNxDqWopdUrxqkFKl/6YtEWsCYrsInADnPXH5sdO6oT3HP31HOE0A9eO - eVbZYV7s2B/GFTlEjCSpatGQ4yqdKDhkRobMwZ2S7GjsEX+sED6aFJG5hAZKhO5ZTGLELHDdv/UF - H5xfesEM+k9TySEFbxsmhAeXsrZFLDlBAiZhqjOaRsgzKuBribhC2D5eEY0LZKADj4ThsRkm0AL7 - GV5m1ESuIOkECxSEJKe3/ZVwjpiAC08tieZpAsH/NVwN/wHQiK4AUwYAAA== - headers: - content-encoding: [gzip] - content-length: ['499'] - content-type: [application/json] - status: {code: 200, message: OK} - request: body: null headers: {} @@ -439,13 +354,13 @@ interactions: body: {string: !!python/unicode "{\n \"card_holds\": [\n {\n \"status\": \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"card\": \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \"debit\": null\n },\n \"updated_at\": - \"2014-07-28T12:44:10.526231Z\",\n \"created_at\": \"2014-07-28T12:44:10.311057Z\",\n - \ \"transaction_number\": \"HL886-348-7865\",\n \"expires_at\": \"2014-08-04T12:44:10.436799Z\",\n + \"2014-08-06T16:10:38.643314Z\",\n \"created_at\": \"2014-08-06T16:10:38.441152Z\",\n + \ \"transaction_number\": \"HL139-329-1699\",\n \"expires_at\": \"2014-08-13T16:10:38.557152Z\",\n \ \"failure_reason\": null,\n \"currency\": \"USD\",\n \"amount\": 2091,\n \"meta\": {\n \"state\": \"new\",\n \"participant_id\": - \"2\"\n },\n \"href\": \"/card_holds/HL6MShgF10sXHw6r21W3OTLX\",\n + \"2\"\n },\n \"href\": \"/card_holds/HL1BndAv7I2I7BPg7Tl25oqY\",\n \ \"failure_reason_code\": null,\n \"voided_at\": null,\n \"id\": - \"HL6MShgF10sXHw6r21W3OTLX\"\n }\n ],\n \"links\": {\n \"card_holds.events\": + \"HL1BndAv7I2I7BPg7Tl25oqY\"\n }\n ],\n \"links\": {\n \"card_holds.events\": \"/card_holds/{card_holds.id}/events\",\n \"card_holds.card\": \"/cards/{card_holds.card}\",\n \ \"card_holds.debits\": \"/card_holds/{card_holds.id}/debits\",\n \"card_holds.debit\": \"/debits/{card_holds.debit}\"\n }\n}"} @@ -454,43 +369,44 @@ interactions: content-type: [application/json] status: {code: 201, message: CREATED} - request: - body: '{"amount": 2092, "description": "janet"}' + body: '{"amount": 2092, "meta": {"exchange_id": 4, "participant_id": 2}, "description": + "janet"}' headers: {} method: POST - uri: https://api.balancedpayments.com:443/card_holds/HL6MShgF10sXHw6r21W3OTLX/debits + uri: https://api.balancedpayments.com:443/card_holds/HL1BndAv7I2I7BPg7Tl25oqY/debits response: body: {string: !!python/unicode "{\n \"errors\": [\n {\n \"status\": \"Bad Request\",\n \"category_code\": \"request\",\n \"additional\": null,\n \"status_code\": 400,\n \"category_type\": \"request\",\n \ \"extras\": {\n \"amount\": \"\\\"2092\\\" must be <= 2091\"\n - \ },\n \"request_id\": \"OHMdf6d676e165411e4b88e02b12035401b\",\n + \ },\n \"request_id\": \"OHM35123eac1d8411e4aecc02b12035401b\",\n \ \"description\": \"Invalid field [amount] - \\\"2092\\\" must be <= - 2091 Your request id is OHMdf6d676e165411e4b88e02b12035401b.\"\n }\n ]\n}"} + 2091 Your request id is OHM35123eac1d8411e4aecc02b12035401b.\"\n }\n ]\n}"} headers: content-length: ['444'] content-type: [application/json] status: {code: 400, message: BAD REQUEST} - request: - body: '{"status": "succeeded", "transaction_number": "HL886-348-7865", "description": - "janet", "amount": 2091, "created_at": "2014-07-28T12:44:10.311057Z", "card_href": - "/cards/CC3MHBsW6vHaL3suLVdTVbO9", "updated_at": "2014-07-28T12:44:10.526231Z", - "expires_at": "2014-08-04T12:44:10.436799Z", "failure_reason": null, "currency": + body: '{"status": "succeeded", "transaction_number": "HL139-329-1699", "description": + "janet", "amount": 2091, "created_at": "2014-08-06T16:10:38.441152Z", "card_href": + "/cards/CC3MHBsW6vHaL3suLVdTVbO9", "updated_at": "2014-08-06T16:10:38.643314Z", + "expires_at": "2014-08-13T16:10:38.557152Z", "failure_reason": null, "currency": "USD", "is_void": true, "meta": {"state": "cancelled", "participant_id": "2"}, - "debit": null, "failure_reason_code": null, "voided_at": null, "id": "HL6MShgF10sXHw6r21W3OTLX"}' + "debit": null, "failure_reason_code": null, "voided_at": null, "id": "HL1BndAv7I2I7BPg7Tl25oqY"}' headers: {} method: PUT - uri: https://api.balancedpayments.com:443/card_holds/HL6MShgF10sXHw6r21W3OTLX + uri: https://api.balancedpayments.com:443/card_holds/HL1BndAv7I2I7BPg7Tl25oqY response: body: {string: !!python/unicode "{\n \"card_holds\": [\n {\n \"status\": \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"card\": \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \"debit\": null\n },\n \"updated_at\": - \"2014-07-28T12:44:11.365477Z\",\n \"created_at\": \"2014-07-28T12:44:10.311057Z\",\n - \ \"transaction_number\": \"HL886-348-7865\",\n \"expires_at\": \"2014-08-04T12:44:10.436799Z\",\n + \"2014-08-06T16:10:39.911789Z\",\n \"created_at\": \"2014-08-06T16:10:38.441152Z\",\n + \ \"transaction_number\": \"HL139-329-1699\",\n \"expires_at\": \"2014-08-13T16:10:38.557152Z\",\n \ \"failure_reason\": null,\n \"currency\": \"USD\",\n \"amount\": 2091,\n \"meta\": {\n \"state\": \"cancelled\",\n \"participant_id\": - \"2\"\n },\n \"href\": \"/card_holds/HL6MShgF10sXHw6r21W3OTLX\",\n - \ \"failure_reason_code\": null,\n \"voided_at\": \"2014-07-28T12:44:11.365480Z\",\n - \ \"id\": \"HL6MShgF10sXHw6r21W3OTLX\"\n }\n ],\n \"links\": {\n + \"2\"\n },\n \"href\": \"/card_holds/HL1BndAv7I2I7BPg7Tl25oqY\",\n + \ \"failure_reason_code\": null,\n \"voided_at\": \"2014-08-06T16:10:39.911791Z\",\n + \ \"id\": \"HL1BndAv7I2I7BPg7Tl25oqY\"\n }\n ],\n \"links\": {\n \ \"card_holds.events\": \"/card_holds/{card_holds.id}/events\",\n \"card_holds.card\": \"/cards/{card_holds.card}\",\n \"card_holds.debits\": \"/card_holds/{card_holds.id}/debits\",\n \ \"card_holds.debit\": \"/debits/{card_holds.debit}\"\n }\n}"} @@ -499,18 +415,18 @@ interactions: content-type: [application/json] status: {code: 200, message: OK} - request: - body: '{"meta": {"seq": 3}}' + body: '{"meta": {"seq": 4}}' headers: {} method: POST uri: https://api.balancedpayments.com:443/customers response: body: {string: !!python/unicode "{\n \"customers\": [\n {\n \"name\": null,\n \"links\": {\n \"source\": null,\n \"destination\": - null\n },\n \"updated_at\": \"2014-07-28T12:44:12.158196Z\",\n \"created_at\": - \"2014-07-28T12:44:12.031864Z\",\n \"dob_month\": null,\n \"id\": - \"CU6OOh9HBCcXISrxLboPWJbr\",\n \"phone\": null,\n \"href\": \"/customers/CU6OOh9HBCcXISrxLboPWJbr\",\n + null\n },\n \"updated_at\": \"2014-08-06T16:10:40.816863Z\",\n \"created_at\": + \"2014-08-06T16:10:40.687848Z\",\n \"dob_month\": null,\n \"id\": + \"CU1DTFMftE4lvobMCsSFSonP\",\n \"phone\": null,\n \"href\": \"/customers/CU1DTFMftE4lvobMCsSFSonP\",\n \ \"merchant_status\": \"no-match\",\n \"meta\": {\n \"seq\": - \"3\"\n },\n \"dob_year\": null,\n \"address\": {\n \"city\": + \"4\"\n },\n \"dob_year\": null,\n \"address\": {\n \"city\": null,\n \"line2\": null,\n \"line1\": null,\n \"state\": null,\n \"postal_code\": null,\n \"country_code\": null\n },\n \ \"business_name\": null,\n \"ssn_last4\": null,\n \"email\": @@ -540,16 +456,16 @@ interactions: \ \"customer\": null\n },\n \"fingerprint\": \"9ea2b317b53183f5a93ba23a594a0b8a0f2183ea9cc338e0964755cd9df71b99\",\n \ \"cvv_result\": null,\n \"number\": \"xxxxxxxxxxxx4448\",\n \"avs_postal_match\": null,\n \"expiration_month\": 12,\n \"meta\": {},\n \"id\": - \"CC6PZQCKGZAQsuwLSTBbRqId\",\n \"category\": \"other\",\n \"type\": + \"CC1EVcqDEt16H3uaDsjp7kqN\",\n \"category\": \"other\",\n \"type\": \"credit\",\n \"cvv_match\": null,\n \"bank_name\": \"CREDIT AGRICOLE BANK POLSKA, S.A.\",\n \"avs_street_match\": null,\n \"brand\": - \"Visa\",\n \"updated_at\": \"2014-07-28T12:44:13.094156Z\",\n \"address\": + \"Visa\",\n \"updated_at\": \"2014-08-06T16:10:41.600581Z\",\n \"address\": {\n \"city\": null,\n \"line2\": null,\n \"line1\": null,\n \ \"state\": null,\n \"postal_code\": null,\n \"country_code\": null\n },\n \"can_debit\": true,\n \"name\": null,\n \"expiration_year\": 2020,\n \"cvv\": null,\n \"is_verified\": true,\n \"avs_result\": - null,\n \"can_credit\": false,\n \"href\": \"/cards/CC6PZQCKGZAQsuwLSTBbRqId\",\n - \ \"created_at\": \"2014-07-28T12:44:13.094153Z\"\n }\n ],\n \"links\": + null,\n \"can_credit\": false,\n \"href\": \"/cards/CC1EVcqDEt16H3uaDsjp7kqN\",\n + \ \"created_at\": \"2014-08-06T16:10:41.600578Z\"\n }\n ],\n \"links\": {\n \"cards.card_holds\": \"/cards/{cards.id}/card_holds\",\n \"cards.customer\": \"/customers/{cards.customer}\",\n \"cards.disputes\": \"/cards/{cards.id}/disputes\",\n \ \"cards.debits\": \"/cards/{cards.id}/debits\"\n }\n}"} @@ -561,80 +477,58 @@ interactions: content-type: [application/json] status: {code: 201, message: CREATED} - request: - body: '{"links": {"customer": "/customers/CU6OOh9HBCcXISrxLboPWJbr"}, "cvv_result": + body: '{"links": {"customer": "/customers/CU1DTFMftE4lvobMCsSFSonP"}, "cvv_result": null, "number": "xxxxxxxxxxxx4448", "avs_postal_match": null, "expiration_month": - 12, "meta": {}, "id": "CC6PZQCKGZAQsuwLSTBbRqId", "category": "other", "type": + 12, "meta": {}, "id": "CC1EVcqDEt16H3uaDsjp7kqN", "category": "other", "type": "credit", "cvv_match": null, "bank_name": "CREDIT AGRICOLE BANK POLSKA, S.A.", - "avs_street_match": null, "brand": "Visa", "updated_at": "2014-07-28T12:44:13.094156Z", + "avs_street_match": null, "brand": "Visa", "updated_at": "2014-08-06T16:10:41.600581Z", "fingerprint": "9ea2b317b53183f5a93ba23a594a0b8a0f2183ea9cc338e0964755cd9df71b99", "can_debit": true, "customer": null, "name": null, "expiration_year": 2020, "cvv": null, "is_verified": true, "avs_result": null, "can_credit": false, "created_at": - "2014-07-28T12:44:13.094153Z", "address": {"city": null, "line2": null, "line1": + "2014-08-06T16:10:41.600578Z", "address": {"city": null, "line2": null, "line1": null, "state": null, "postal_code": null, "country_code": null}}' headers: {} method: PUT - uri: https://api.balancedpayments.com:443/cards/CC6PZQCKGZAQsuwLSTBbRqId + uri: https://api.balancedpayments.com:443/cards/CC1EVcqDEt16H3uaDsjp7kqN response: body: string: !!binary | - H4sIAAAAAAAAA41U247aMBB936+I8rwLuULCG6SrLQUVFuhFVFVkx85ibeJQ22FBKP9eOxcILKs2 - D5FyJjNzzsyxj3eapkeAIa4PtF/yQ9OO5VvCCaGvCm4A9WfORZZiJlE9+NabzTb+51EQ/Rwv2X4K - s/mPL5DpdX5x3xSKCX3BbMsIFSrRx8CCttmHrm16duwC34bAsoHrO8CAHjBiS+IY+FFk2x42/J7T - d90I+Sjum9D39VPhaLcLGeZ5ourSPElOEZqnsKK5bz2O43jnbLDj4TbjAiRhCkS0ua6B91vCgCAZ - DdOMChU3rVOHFAughnOWSVA5lqA3Xz8Hk6f18Jnnb9PlagQXf8aoxRoI/JKxg/o7ExtJ81RUHLZY - wRHDiIhLoTc5QkBfQwrSMitYPH4ar7Th02IczKaP2mj4daLNZ9PlZHivLTvDzqV2LhjG4rZ2yAAt - 5XwnHJzT8i2S5FEIykVahuk8GP0Hy1uZ1sBxBqbd6Vk9w3PXrU4IyRVd+4gIJb+9sspw2LoNm+9h - uTihZF8VqTcaZehGMMpyKtghbEXfuTUCNEQYyvEPNMFyfPZUNeYLn7U8csBAnQvLsIy2Qa8Y6oSH - O8xITLCa70UDZcjbdlacak8MtBgk/Mxqw3AsC+nd8hR3/8N+DP9jh4bvmK69rg5yIQf0Wwm6ug+q - W6OjuoabLCkvkIbEseTSIagoWdXxaipNXusm6TbXCu/WmQ1Q1D6qkxDh21zgD1qdoheNylV+lFHF - pLjirvgLKWEQ5QsFAAA= + H4sIAAAAAAAAA41U226jMBB971cgnlticydv2STdrXpVk+1DVytksGm8AUNtEzWK+PfFXBKSpqvl + AYkzzMw5M8feXWiaHiOOhT7WftUfmrZr3jWcUrZWcA+oP0sh84zwGtWnP+FseX2fyLmdbvLofioW + 14ucPeldfnXZF0ooeyO84JRJlRgQZEYW9CLHgr6VOCiwImRayAlsBCIfgcSscYKCOLYsn4DAtT3H + iXGAEw9GQaDvC8ebTciJKFNVl5Vpuo+wMotamh+Dx7Zt/5CNNiIsciFRGmZIxqvTGuSjoBxJmrMw + y5lUcWjuO2REIjWcg0yKm7FM4fwlfp/NJXR/WCWaiT+Ft35/GLBGkrzlfKv+zuWqprkvKrcFUXDM + CabyWOhZjhFi65ChrMmaPs9nN0tt8v35Zvp4N9e+TR5utafHu8Xt5FJbGBPjWLuQnBB5XnvEEWvk + vFCBDmllgWvyOETNIk0A7SvgXwF3Cd0xBGPbNCAIfNd7HXTCuF7RqY+oVPKHK2sNR8zzMPwM14uT + SvZJkW6jcY7PBOO8ZJJvw0H0k1tjxEJMonr8Y03ykhw81Y75yGcDj2wJUufCBCYYGvSEoU5FuCGc + JpSo+R41UIY8b2fFqfPEWEtQKg6sVpwkdSF91Jzi0X/Yj5N/7hAaLgCO57+2B7mqB/RbCTq5D9pb + w1Bdw1WeNhdIT2LXcDEorhpWXbydSp83uElG/bUiRl1mD1Sdj7okTEVRSvJFq330qFGzyq8y2lgt + rrqo/gIsC5QGCwUAAA== headers: content-encoding: [gzip] - content-length: ['584'] + content-length: ['583'] content-type: [application/json] status: {code: 200, message: OK} - request: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU6OOh9HBCcXISrxLboPWJbr + uri: https://api.balancedpayments.com:443/customers/CU1DTFMftE4lvobMCsSFSonP/cards?limit=10&offset=0 response: body: string: !!binary | - H4sIAAAAAAAAA41UwY7TMBC971dEOdNNkw2l2yN7AYS0SIBAIBRNbKNYm9jFdtBWVf6dcZukrh0w - lxxm3nv2vHnx8SZJUtJrIzumdLpLvmMhSY6nL7YEdAyrom/bF1Ot5eLJQicQwrTsFfGBWKdMGy7A - cClGlVFkmNX6PQXDaAUGEWmxzsvV+tWq2H7Ki11Z7vLiNn+5ze8339KZQhSLUNZ3+XZTOhQq66qT - wjT+MJzaYx8+bx4fm/s3rx/I17cf1fP7Wn748q5WlzP3jRT+gGmj2E9Lz2YHs7gSGk0aEKbSBkxv - jUyFXHVgSHM5rmMGPIvZLwu9SwMH7WwHBsofDShVTHubItwcPCDuCVfKiuVyHpbtxX0vUGQvsdFW - RNKFJpG9MOrgdoNB6l7jPbSullKntaha0Kb0x2Qd8DYo8qvADXjWD5sfO6kT3kv0b+cIpxm6dsqz - zo7zYsf+MK7IIRJQtGpkS0+rdKLgkDkdMgd3TrKjUYN4qoCcTIrIXEMDJcpqHpMYMQtc92/9iw/O - L71gBns2TAlMwf8NE8KDS1nbIpacIQGTcr3vDYuQZ1TANwqEBmIfr4jGFTLQwUeiF7EZJtAC+ze+ - zNBGrqDYBAsUpKLnt/0f4RwxARefWhrN0wTC/2u4Gf4A2ILrwFMGAAA= - headers: - content-encoding: [gzip] - content-length: ['497'] - content-type: [application/json] - status: {code: 200, message: OK} -- request: - body: null - headers: {} - method: GET - uri: https://api.balancedpayments.com:443/customers/CU6OOh9HBCcXISrxLboPWJbr/cards?limit=10&offset=0 - response: - body: - string: !!binary | - H4sIAAAAAAAAA6VUbW/aMBD+3l8R+cM+tZBXIEjVRFnVsaJCS/cipilyYqdYTZzMdhio4r/PdhIS - KNWmjQ+RuPPdPXfPc/dyZhggggxxMDS+yz+G8aK/0pwQ+qzMtUG9LLjIUsykFYw/92azlf/xahx9 - myzYZhpm86+fQgaq+N15nSgm9AmznBEqVKCPoR06Vj/0HGvgxB70nRDaDvR8F5rhAJqxLe0Y+lHk - OANs+j2373kR8lHct0LfB/vE0XodMMyLROWlRZLsPbRIwxLmpvVzXXfQRMM1D/KMC5gEKRTR6jgH - 3uSEQUEyGqQZFcpv2fsKKRZQDadpkyA9lnFvvrwf394sR/e8+DVdPF6FDz8nqIUaCvyUsa16nYmV - hLlPKrY5VuaIYUTEYaMnMYaQPgcUpjpq/HD9YfJojG4eJuPZ9Nq4Gt3dGvPZdHE7OjcWnVHnsHcu - GMbidO8hg1S384Vw2IQVOZLgUQA1kbZpuRdm/8IePFr20HWHltPp2T1z4C1blRCSFB3riAjVfpuy - UnDYPm22XpslcUK1fZSkYjTK0AlnlBVUsG3Q8r5SawRpgHAoxz80BCtwo6lyzAc6a2lki6HaC9u0 - zbZAjxACwoM1ZiQmWM33oIAS5Gk5K0yVJoZGDBPeoFoxHMtEoKu3uPsX8mP4Dxyavmt5zrJc5J0c - 0A/VEKgVrycGEsi1CLr1TZC137gIJbT3CUmJuLTMd1kccywuzUokgOLN4QKDpqnq4Px7cl1VArUq - VkBZXVpqQ87wmmSFkmjDLRCZPAwqriQTxIT9f8NydPpeHN3W8gJ3EOF5IbACUtP5olntELTr7r0V - oNLTvsgtKqq42rurR10FyaTBKkv01T9RSr2q/AfF9Fq8EVP5VIdnu9+TvyXqVwYAAA== + H4sIAAAAAAAAA6VUTW/bMAy991cYOuzUJpLtJHaAYsiSdCv6iabrocNgyLbcaLUlV5KDBkX++yR/ + xE6aDsOWg4GQIvnI98i3I8sCERaxBGPrh/5jWW/lV5tTyp6NuTGYl4VUPCNCW8H0O5rdn10lau6m + Kx5eTeXibMHZLajjN8dNooSyJyJyQZkygT7BduigUThwkOckA+w7IbYdPPBdDEMPw8TWdoL9KHIc + j0B/6I4Ggyj242SEQt8H28TRahUIIovU5GVFmm49rMjCCuZr5+e6rtdG45UMci4VToMMq2i5n4O8 + 5lRgRTkLMs6U8SN7WyEjCpvhtG3SuBzLFM0fopfZXKHhN6fAM/krHz2/XHdQY0WeuFib11wtNcxt + UrXOiTFHgsRU7TZ6EGOI2XPAcFZGTe/ms/N7a/L17nx6czm3vkyuL6zbm8vFxeTYWvQmvd3epRKE + qMO9hwKzsp0HKnEbVuSxBh8HuCTShsg9gd4JHN6j4RjBsWv3EPS94eixUymONUX7OqLKtN+lrBIc + sQ+b0XuzJk6ZtveS1IxGPD7gjHjBlFgHHe87tUaYBTEJ9fjHlhIFaTVVjXlHZx2NrAk2e2FDG3YF + uocQUBmsiKAJJWa+OwWMIA/L2WCqNTG2EpzKFtVSkEQnAv1yi/t/IT9B/sgh6g0hHIy8x2qRN3pA + P01DoFF8OTGQYlmKoN/cBF37g4tQQfuc0oyqUwQ/8SSRRJ3CWiSAkdfdBQZtU/XB+ffkZVUNFNWs + gKq6tjSGXJAV5YWRaMstUFwfBhNXkQkSKv6/YT268l7s3dbqAvdiKvNCEQOkofOtZLVH401/660B + VZ7uRe5QUcc13k0z6jpIJw2WPC2v/oFS5lXt3ylWrsUHMbXPdHi0+Q2h8zE4VwYAAA== headers: content-encoding: [gzip] content-length: ['676'] @@ -645,31 +539,31 @@ interactions: "bob"}' headers: {} method: POST - uri: https://api.balancedpayments.com:443/cards/CC6PZQCKGZAQsuwLSTBbRqId/card_holds + uri: https://api.balancedpayments.com:443/cards/CC1EVcqDEt16H3uaDsjp7kqN/card_holds response: body: {string: !!python/unicode "{\n \"errors\": [\n {\n \"status\": \"Payment Required\",\n \"category_code\": \"card-declined\",\n \"additional\": \"Account Frozen\",\n \"status_code\": 402,\n \"category_type\": - \"banking\",\n \"extras\": {},\n \"request_id\": \"OHMe1ca5954165411e4ac8902b12035401b\",\n - \ \"description\": \"R758: Account Frozen. Your request id is OHMe1ca5954165411e4ac8902b12035401b.\"\n + \"banking\",\n \"extras\": {},\n \"request_id\": \"OHM3750c8d21d8411e4ac8902b12035401b\",\n + \ \"description\": \"R758: Account Frozen. Your request id is OHM3750c8d21d8411e4ac8902b12035401b.\"\n \ }\n ]\n}"} headers: content-length: ['387'] content-type: [application/json] status: {code: 402, message: PAYMENT REQUIRED} - request: - body: '{"meta": {"seq": 4}}' + body: '{"meta": {"seq": 5}}' headers: {} method: POST uri: https://api.balancedpayments.com:443/customers response: body: {string: !!python/unicode "{\n \"customers\": [\n {\n \"name\": null,\n \"links\": {\n \"source\": null,\n \"destination\": - null\n },\n \"updated_at\": \"2014-07-28T12:44:15.686124Z\",\n \"created_at\": - \"2014-07-28T12:44:15.567519Z\",\n \"dob_month\": null,\n \"id\": - \"CU6SMkCwTyQHAjXzmtSm0CRO\",\n \"phone\": null,\n \"href\": \"/customers/CU6SMkCwTyQHAjXzmtSm0CRO\",\n + null\n },\n \"updated_at\": \"2014-08-06T16:10:43.645536Z\",\n \"created_at\": + \"2014-08-06T16:10:43.529220Z\",\n \"dob_month\": null,\n \"id\": + \"CU1H61sY8q0ahFI1LKltdoOY\",\n \"phone\": null,\n \"href\": \"/customers/CU1H61sY8q0ahFI1LKltdoOY\",\n \ \"merchant_status\": \"no-match\",\n \"meta\": {\n \"seq\": - \"4\"\n },\n \"dob_year\": null,\n \"address\": {\n \"city\": + \"5\"\n },\n \"dob_year\": null,\n \"address\": {\n \"city\": null,\n \"line2\": null,\n \"line1\": null,\n \"state\": null,\n \"postal_code\": null,\n \"country_code\": null\n },\n \ \"business_name\": null,\n \"ssn_last4\": null,\n \"email\": @@ -699,16 +593,16 @@ interactions: \ \"customer\": null\n },\n \"fingerprint\": \"1e2c425a579a1c7f27a037f0662b855035ceadbff18a0adbe785fb07e2afe1cc\",\n \ \"cvv_result\": null,\n \"number\": \"xxxxxxxxxxxx4242\",\n \"avs_postal_match\": null,\n \"expiration_month\": 12,\n \"meta\": {},\n \"id\": - \"CC6TLLgPxyk72mp6wYSQ54YF\",\n \"category\": \"other\",\n \"type\": + \"CC1Iivi5I2HmI7btXkS1ze6s\",\n \"category\": \"other\",\n \"type\": \"credit\",\n \"cvv_match\": null,\n \"bank_name\": \"\",\n \"avs_street_match\": - null,\n \"brand\": \"Visa\",\n \"updated_at\": \"2014-07-28T12:44:16.442981Z\",\n + null,\n \"brand\": \"Visa\",\n \"updated_at\": \"2014-08-06T16:10:44.599088Z\",\n \ \"address\": {\n \"city\": null,\n \"line2\": null,\n \ \"line1\": null,\n \"state\": null,\n \"postal_code\": null,\n \"country_code\": null\n },\n \"can_debit\": true,\n \ \"name\": null,\n \"expiration_year\": 2020,\n \"cvv\": null,\n \ \"is_verified\": true,\n \"avs_result\": null,\n \"can_credit\": - false,\n \"href\": \"/cards/CC6TLLgPxyk72mp6wYSQ54YF\",\n \"created_at\": - \"2014-07-28T12:44:16.442979Z\"\n }\n ],\n \"links\": {\n \"cards.card_holds\": + false,\n \"href\": \"/cards/CC1Iivi5I2HmI7btXkS1ze6s\",\n \"created_at\": + \"2014-08-06T16:10:44.599085Z\"\n }\n ],\n \"links\": {\n \"cards.card_holds\": \"/cards/{cards.id}/card_holds\",\n \"cards.customer\": \"/customers/{cards.customer}\",\n \ \"cards.disputes\": \"/cards/{cards.id}/disputes\",\n \"cards.debits\": \"/cards/{cards.id}/debits\"\n }\n}"} @@ -720,34 +614,34 @@ interactions: content-type: [application/json] status: {code: 201, message: CREATED} - request: - body: '{"links": {"customer": "/customers/CU6SMkCwTyQHAjXzmtSm0CRO"}, "cvv_result": + body: '{"links": {"customer": "/customers/CU1H61sY8q0ahFI1LKltdoOY"}, "cvv_result": null, "number": "xxxxxxxxxxxx4242", "avs_postal_match": null, "expiration_month": - 12, "meta": {}, "id": "CC6TLLgPxyk72mp6wYSQ54YF", "category": "other", "type": + 12, "meta": {}, "id": "CC1Iivi5I2HmI7btXkS1ze6s", "category": "other", "type": "credit", "cvv_match": null, "bank_name": "", "avs_street_match": null, "brand": - "Visa", "updated_at": "2014-07-28T12:44:16.442981Z", "fingerprint": "1e2c425a579a1c7f27a037f0662b855035ceadbff18a0adbe785fb07e2afe1cc", + "Visa", "updated_at": "2014-08-06T16:10:44.599088Z", "fingerprint": "1e2c425a579a1c7f27a037f0662b855035ceadbff18a0adbe785fb07e2afe1cc", "can_debit": true, "customer": null, "name": null, "expiration_year": 2020, "cvv": null, "is_verified": true, "avs_result": null, "can_credit": false, "created_at": - "2014-07-28T12:44:16.442979Z", "address": {"city": null, "line2": null, "line1": + "2014-08-06T16:10:44.599085Z", "address": {"city": null, "line2": null, "line1": null, "state": null, "postal_code": null, "country_code": null}}' headers: {} method: PUT - uri: https://api.balancedpayments.com:443/cards/CC6TLLgPxyk72mp6wYSQ54YF + uri: https://api.balancedpayments.com:443/cards/CC1Iivi5I2HmI7btXkS1ze6s response: body: string: !!binary | - H4sIAAAAAAAAA41UTW/bMAy991cYPq+JrPoruRUBhh06bF2yYe1QGLJEJ1ps2ZDlNF7g/z7JH4mT - pth8MGxSj3wkn3i4sSybEslKe2790j+WdWjf2pxysTXmwWBOVqXKM5Daai+++8vP28Xrqn78dP/7 - 559MLTO0+PbF7vHNhyFQwsUaZCG5UAboAKYu9ogXzIhDgwQHBN0FCfJ9HIeeh+48CoTFSeKEBOkP - CEIviVEAmCTgUGofA9PdLpJQVqmJK6o0PXpElcUdzf3ocbGLT2iyK6MiLxVJo4wourmMAfuCS6J4 - LqIsF8r4HXzMkIEipjmnMjlr27LwVw8P66/7ehvgrPBfn5aPnvv0ccSaKFjnsjanc7XRNI9BVV2A - MVMJjKvzQq9yjInYRoJkLeq8tFJJAHW9tFgS0bL9wUtyglUF09xYRNo5YeS4tyi4xeHKwXPXnTv+ - JMTYReHzKBNjegKXMuHKVDeeSKcnwNfNzluznosyVV0E6QdGc3bFSfNKKFlHI+8bMVIiIgax7u7c - UrKCk2S6Lp7JaCSBGoiRPUYYjfV3wdDmZbQDyRMOpr9nCYzerqvVcOpHPrcSkpYnVhsJiQ5kT9tL - Ov0PdUn4xwxdF8+C2XN3TxvdoBdT0MV175bCxGSNNnna7oeBxKHlMuGsaVn1/q4rA260KKbD1iin - PXIwNL2OehDjZVEpeCfV0XuWqB3le4jOp4trbpq/Q/Cq6eoEAAA= + H4sIAAAAAAAAA41U227bMAx971cYfm4TSfEteS0wNNiAPeyCtcVgyBLdCLFlT5KDZoH/fZIviZOm + WP1g2KQOeUge8XDjeT6jimt/5T3bH887dG9rLoTcOvNocCcbbaoSlLX69z/wQ4T1Y/IH0c2nNf7y + uTC8+vroD/j2dgyUC/kCqlZCGgfEQFhAQhrGS4pZnJOYokWcoygiWRKGaBEyoDzLc5xQZD8gTsI8 + QzEQmgNmzD8GZrtdqkA3hYsrm6I4emRTZj3N18kTkICc0HSn07rShhZpSQ3bXMaA11ooakQl07KS + xvkxOWYowVDXnFOZgndtucdrsRPhmjyU6zgzv7bf8F+I9IQ1NfBSqb07XZmNpXkMavY1ODNTwIU5 + L/Qqx4zKbSpp2aHOS9NGAZjrpWWKyo7tT6HpCdbU3HLjKe3mRBAO7lByh6LvOFphtAqC2TIJk0Xy + NMnEuZ3ApUyEcdVNJ9LrCch1M35rtnMxrqqLIMPAWMWvOFnVSKP26cT7RoyMypRDZru78oxq4CSZ + votnMppIYA/UyZ4ggqb6u2DoC53uQIlcgOvvWQKnt+tqdZyGka+8nBb6xGqjILeB/Hl3SecfUJeC + /8wwXC5REj7197S1DfrtCrq47v1SmLms6aYquv0wkjh0XGaCtx2rwd93ZcRNFsV83Bp6PiBHQzvo + aABxoevGwDupjt6zRN0o30P0Pltce9P+AyYgRuPqBAAA headers: content-encoding: [gzip] - content-length: ['551'] + content-length: ['546'] content-type: [application/json] status: {code: 200, message: OK} - request: @@ -761,16 +655,16 @@ interactions: \ \"customer\": null\n },\n \"fingerprint\": \"d34d613121ae61ae732d86eaf09929617bd3f5ccc60f7364d32ae1afc5f81745\",\n \ \"cvv_result\": null,\n \"number\": \"xxxxxxxxxxxx4242\",\n \"avs_postal_match\": null,\n \"expiration_month\": 12,\n \"meta\": {},\n \"id\": - \"CC6V7qZ6DESWZ76Ta29niOlD\",\n \"category\": \"other\",\n \"type\": + \"CC1K1gP7SunZXxzUAe188mGx\",\n \"category\": \"other\",\n \"type\": \"credit\",\n \"cvv_match\": null,\n \"bank_name\": \"\",\n \"avs_street_match\": - null,\n \"brand\": \"Visa\",\n \"updated_at\": \"2014-07-28T12:44:17.648341Z\",\n + null,\n \"brand\": \"Visa\",\n \"updated_at\": \"2014-08-06T16:10:46.133144Z\",\n \ \"address\": {\n \"city\": null,\n \"line2\": null,\n \ \"line1\": null,\n \"state\": null,\n \"postal_code\": null,\n \"country_code\": null\n },\n \"can_debit\": true,\n \ \"name\": null,\n \"expiration_year\": 2030,\n \"cvv\": null,\n \ \"is_verified\": true,\n \"avs_result\": null,\n \"can_credit\": - false,\n \"href\": \"/cards/CC6V7qZ6DESWZ76Ta29niOlD\",\n \"created_at\": - \"2014-07-28T12:44:17.648338Z\"\n }\n ],\n \"links\": {\n \"cards.card_holds\": + false,\n \"href\": \"/cards/CC1K1gP7SunZXxzUAe188mGx\",\n \"created_at\": + \"2014-08-06T16:10:46.133142Z\"\n }\n ],\n \"links\": {\n \"cards.card_holds\": \"/cards/{cards.id}/card_holds\",\n \"cards.customer\": \"/customers/{cards.customer}\",\n \ \"cards.disputes\": \"/cards/{cards.id}/disputes\",\n \"cards.debits\": \"/cards/{cards.id}/debits\"\n }\n}"} @@ -782,98 +676,76 @@ interactions: content-type: [application/json] status: {code: 201, message: CREATED} - request: - body: '{"links": {"customer": "/customers/CU6SMkCwTyQHAjXzmtSm0CRO"}, "cvv_result": + body: '{"links": {"customer": "/customers/CU1H61sY8q0ahFI1LKltdoOY"}, "cvv_result": null, "number": "xxxxxxxxxxxx4242", "avs_postal_match": null, "expiration_month": - 12, "meta": {}, "id": "CC6V7qZ6DESWZ76Ta29niOlD", "category": "other", "type": + 12, "meta": {}, "id": "CC1K1gP7SunZXxzUAe188mGx", "category": "other", "type": "credit", "cvv_match": null, "bank_name": "", "avs_street_match": null, "brand": - "Visa", "updated_at": "2014-07-28T12:44:17.648341Z", "fingerprint": "d34d613121ae61ae732d86eaf09929617bd3f5ccc60f7364d32ae1afc5f81745", + "Visa", "updated_at": "2014-08-06T16:10:46.133144Z", "fingerprint": "d34d613121ae61ae732d86eaf09929617bd3f5ccc60f7364d32ae1afc5f81745", "can_debit": true, "customer": null, "name": null, "expiration_year": 2030, "cvv": null, "is_verified": true, "avs_result": null, "can_credit": false, "created_at": - "2014-07-28T12:44:17.648338Z", "address": {"city": null, "line2": null, "line1": + "2014-08-06T16:10:46.133142Z", "address": {"city": null, "line2": null, "line1": null, "state": null, "postal_code": null, "country_code": null}}' headers: {} method: PUT - uri: https://api.balancedpayments.com:443/cards/CC6V7qZ6DESWZ76Ta29niOlD - response: - body: - string: !!binary | - H4sIAAAAAAAAA41U32+bMBB+71+BeG4TbBxD8lalk/YyVVuydso0Icc2jRcwzDZZs4j/fTY/EpKm - 05BA4s7f3Xd3n+9w43k+JYppf+Z9tz+ed2i+1pwJuXXm3uBOVtoUOVfW6s+/4sWn7fz3cv/54/3P - b39ys8iD+ZdHv8PXt32gVMgXrkolpHFAFiKGQQggIBzbNwohizEnaTCdwikG0ZqF6YRSioM0CjFi - ISQckJRO0hhEaOIfA9PdLlFcV5mLK6ssO3pkla9bmq+DB0EET2iy00lZaEOyJCeGbi5j8NdSKGJE - IZO8kMb5ATxmyLkhrjmnMgVr2jLHT9GvFX74sHheRXhJ4FSKx+xhwJoY/lKovTtdmI2leQxq9iV3 - Zqo4E+a80Ksc10RuE0nyBnVemjaKc3O9tLUismH7JDQ5waqSWW4sIc2cYADQXRDdwXgJ4AyhGYhH - QYQRRqtBJsbsBC5lIoyrbjiRVk8cXjeDt2Y7F+OqugjSDYwW7IqTFpU0ap8MvG/ESIlMGF/b7s48 - oyp+kkzbxTMZDSSw58TJHgZhMNTfBUNf6GTHlUgFd/09S+D0dl2tjlM38pmXkkyfWG0UT20gf9xc - 0vF/qEvxf84wGmEUh2G8au9pbRv0wxV0cd3bpTByWZNNkTX7oSdxaLiMBKsbVp2/7UqPGyyKcb81 - 9LhD9oa601EHYkKXleHvpDp6zxI1o3wP0fpscfVN/RfqZT+Q6gQAAA== - headers: - content-encoding: [gzip] - content-length: ['553'] - content-type: [application/json] - status: {code: 200, message: OK} -- request: - body: null - headers: {} - method: GET - uri: https://api.balancedpayments.com:443/customers/CU6SMkCwTyQHAjXzmtSm0CRO + uri: https://api.balancedpayments.com:443/cards/CC1K1gP7SunZXxzUAe188mGx response: body: string: !!binary | - H4sIAAAAAAAAA41UwY7TMBC971dEOdNNE6XdpTfUCxeEYBcJgVA0tY1imtjFdoBS5d8Zt0nq2gFz - yWHmvWfPmxef7pIkJZ02smVKp5vkMxaS5HT+YktAy7AquqZ5MdYaLvYWOoIQpmWniA/EOmXacAGG - SzGoDCL9pNYdKBhGKzCISItlXi6WD4vi8TkvNmW5yVf368d1XpSf0olCFItQVuuHVf7SoVC5q1op - TO0Pw6k9dvth/fRmv/35fHz3+tW3j79b89Qut+/fXs881FL4A6a1Yl8tPZsczOJKaDSpQZhKGzCd - NTIVctGCIfX1uJYZ8Cxm3y20TAMH7WxHBsofDShVTHubItwcPSDuCVfKivlyHpbtxX0vUOQgsdFU - RNKZJpGdMOrodoNBdp3Ge2hdzaVOa1E1oE3pj8la4E1Q5DeB6/GsLzY/dlInvNfo308RTjN07Zxn - nZ2mxQ79fliRQySgaFXLhp5X6UTBIXPaZw7ukmRHYwdiXwE5mxSRuYUGSpTteExiwMxw3b/1Lz44 - v/SMGeyXYUpgCv5vmBAeXMraFrHkAgmYlOtDZ1iEPKECvlEgNBD7eEU0bpCBDj4SnYjNMIJm2D/w - ZYYmcgXFRligIBW9vO3/COeACbj41NJonkYQ/l/9Xf8HeuokgVMGAAA= + H4sIAAAAAAAAA41UbW+bMBD+3l+B+Lwm2BBC8q2qtBd10iZtndZMFXLsI7EKhtkmShbx32fznpRq + A4HEHc/dc3eP73zjOC4lkil37fwyH45zrt/GnHLxYs2dwf5ZKp1nII3VvX9EH0OknqLfHtm//4Q+ + P6Sa5V+e3BZfvesCJVzsQBaSC22BzA9YiHyEEYHQPEsfsygEknirFV6FaLllfrKglIZesvTDgPmY + ACIJXSQRWgYLtw9MD4dYgipTG1eUadp7RJltG5rH0RXgAA9oclBxkStN0jgjmu6vY8Cx4JJonos4 + y4W2foT7DBloYpszlMlZ3ZZ79IB2X5ffSrH5efzzeAcoirIPxxFromGXy5P9O9d7Q7MPqk8FWDOV + wLi+LHSS45aIl1iQrEZdlqa0BNDTpW0lETXbH1yRAVYWzHBjMannhD0U3HrRrRd+R+EaeesgnEWR + Z+7NKBNjZgLXMuHaVjeeSKMnwNNm9Nps5qJtVVdB2oHRnE04aV4KLU/xyPtKjJSImMHWdHftaFnC + IJmmixcyGkngBMTKHnu+N9bfFUOXq/gAkiccbH8vEli9TavVcmpHvnYSkqqB1V5CYgK58/qQzv9D + XRL+MUPk+yjAm+acVqZBz7agq+PeLIWZzRrv87TeDx2Jc81lxllVs2r9TVc63GhRzLutoeYtsjNU + rY5aEOOqKDW8kar3XiSqR/kWovGZ4qqb6i+YkJej6gQAAA== headers: content-encoding: [gzip] - content-length: ['497'] + content-length: ['547'] content-type: [application/json] status: {code: 200, message: OK} - request: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU6SMkCwTyQHAjXzmtSm0CRO/cards?limit=10&offset=0 + uri: https://api.balancedpayments.com:443/customers/CU1H61sY8q0ahFI1LKltdoOY/cards?limit=10&offset=0 response: body: string: !!binary | - H4sIAAAAAAAAA+VWW2/TMBR+36+o8sATa2MnsdNKE0IdiIchGC2DFaHI8WU1zaU4Ttcy9b9jJ2mb - dp2GQGgSVGrUnpNzfC7f+Y7vTjodhxLFCmfQ+WL+dDp31dOIE5nNrHgjsG+Whc5TrozUGX5Eo7ez - 4e14dfnm5bfPP1I9St3hh3dOY79+vnEkZHbD1VzJTFtDwCH1YUAC3CeAYgExcT0sXIRgHAaB6wWU - ExYLAULimh8ch4GIXcwhERxQ6mwd08UiUrwoE+s3K5Nkq8nKNK7DXLY+PvThzposimieF5okUUo0 - nR764Mu5VETLPIvSPNNWD+D2hJRrYouzS1OyqixDNL64uHm/XM0wTOfo9np0GfjXr1tRE81vcrWy - b+d6asLcOtWrObdiqjiTej/RozHGJJtFGUkrq/3UCq0418dTixXJqmivZEF2ZuWcmdhYRKo+QRf4 - py4+heEYwIHvDwDqhhD6bjhpncSY6cAhTKS22bU7UuOJw+NicF9s+qJtVgdOmobRnB1R0rzMtFpF - Le09MFKSRYzHprqDjlYl30GmruIejFoQWHFiYQ9d6LbxdxChI4towZUUktv67h1g8XYcrTampuWD - jiBJsYtqqrgwjpxeNaS9X0CX4o/00PdhH/cn9Zw28P3rQ888nyHgAQgIR+aLPchCxIlw+33YRwDH - zBMBpRS5AnvIZx4kHBBBAxEC7Af7s3C8jE869Ff4+wSdvxp9mmA0JrCfyXfJ+b8w9GHXxchH/n89 - 9N4TD/1j6Hpk6HEX+aHnGeKu+HBtnl8tizmbLVaJnYQUFfP3NnveEM4DW77moxeJTKU+A+6zXIiC - 6zO3QbyT8eX+UnZ2TNZcIn7feXWqCRQ0XXHq041kI5grvpB5affSjtAdnZtlb0m8ZnBHSPXnCZvS - VSR6cF+qb1Xd9o2pVda7is632vWmbLWYyWJeam6j3xB/YyDZurfVNlk0rozDaJon1U3uiJV9q9Hv - 2VW78AGbRmczPFn/BA+kcscrCgAA + H4sIAAAAAAAAA+VW22obMRB9z1eYfehTY0vaqw2hlEAbk0ILSUqSUhatNIpF9uJqtcZO8L9X2ou9 + dhxSWkqgtbGxZ3ZGczlzRo9Hg4HDqOKlMxl8M38Gg8f624hTmd9bcSewT1alLjJQRuqcXuGzAJc3 + 0Q9EZx+m+NN5qnnx+cZp7ddvO0dC5neg5krm2hpiIMwjPvXDMcUsFCSkyA0FCgKSRL6PXJ8B5YkQ + OKLI/IAw8kWCQiBUAGbM2Thmi0WsoKxS6zev0nSjyassacJc9l4e8cjWmi7KeF6UmqZxRjWb7fuA + 5VwqqmWRx1mRa6vHZHNCBpra4mzTlLwuyymeyoX0p+Qsm4aJvr6/wA8QlL2oqYa7Qq3s04WemTA3 + TvVqDlbMFHCpdxM9GGNC8/s4p1lttZtaqRWAPpxaomheR/tVlnRrVs25iY3HtO4TQdg7RtExCi5x + MMFo4nnDceRHbnTbO4lz04F9mEhts+t3pMETkMNi/FRs+qJtVntO2oaxgh9QsqLKtVrFPe0TMDKa + xxwSU93JQKsKtpBpqrgDox4EVkAt7AkiqI+/vQgdWcYLUFJIsPXdOcDi7TBabUxtyycDQdNyG9VM + gTCOnFE9pKNfQJeCF3roj8co8m+bOW3h+9eHnrseD7CLCaYQmE/oEh4FQAUaj8k4wGHCXeEzxgIk + QjfwuEsoYCqYLyIcev7uLBwu46sO/Tm++xJeVPnt9fLh6j3gKMo+Lv+FoQ+GUYTM+78eeveVh/4l + dL0w9MEQuy72SDf0hhW/WxZzui1W06ST0rJm/lG35w3hPLPlGz56l8pM6hOM3hRClKBPUIt4J4fl + 7lJ2tkzWXiJ+33l9qgkUt11xmtONpBPMFSxkUdm9tCV0Rxdm2VsSbxjcEVL9ecKmdDWJ7t2XmlvV + sH9j6pX1sabzjXbdla0Rc1nOKw02+o74WwPJ16ONts2idWUcxrMirW9yB6zsU61+x67ehc/YtDqb + 4dH6J9Yl180rCgAA headers: content-encoding: [gzip] - content-length: ['762'] + content-length: ['753'] content-type: [application/json] status: {code: 200, message: OK} - request: - body: '{"meta": {"seq": 5}}' + body: '{"meta": {"seq": 6}}' headers: {} method: POST uri: https://api.balancedpayments.com:443/customers response: body: {string: !!python/unicode "{\n \"customers\": [\n {\n \"name\": null,\n \"links\": {\n \"source\": null,\n \"destination\": - null\n },\n \"updated_at\": \"2014-07-28T12:44:19.427386Z\",\n \"created_at\": - \"2014-07-28T12:44:19.315286Z\",\n \"dob_month\": null,\n \"id\": - \"CU6X0aCm4qom2lpUvyxszYvv\",\n \"phone\": null,\n \"href\": \"/customers/CU6X0aCm4qom2lpUvyxszYvv\",\n + null\n },\n \"updated_at\": \"2014-08-06T16:10:47.916458Z\",\n \"created_at\": + \"2014-08-06T16:10:47.747801Z\",\n \"dob_month\": null,\n \"id\": + \"CU1LPZ5SkBmi1Spr6shPbNnT\",\n \"phone\": null,\n \"href\": \"/customers/CU1LPZ5SkBmi1Spr6shPbNnT\",\n \ \"merchant_status\": \"no-match\",\n \"meta\": {\n \"seq\": - \"5\"\n },\n \"dob_year\": null,\n \"address\": {\n \"city\": + \"6\"\n },\n \"dob_year\": null,\n \"address\": {\n \"city\": null,\n \"line2\": null,\n \"line1\": null,\n \"state\": null,\n \"postal_code\": null,\n \"country_code\": null\n },\n \ \"business_name\": null,\n \"ssn_last4\": null,\n \"email\": @@ -896,62 +768,18 @@ interactions: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU6X0aCm4qom2lpUvyxszYvv - response: - body: - string: !!binary | - H4sIAAAAAAAAA41UXW+jMBB8769APDclUPqV1/6Fq3TtqUIb2yesGpvaJmou4r93nQAhNne+Fx52 - Z8be2cGHqyRJSWesapg26Sb5hYUkORy/2JLQMKzKTojrsSa4/HDQEYQwozpNfCDWKTOWS7BcyUFl - EOknta6lYBmtwCIiLdZ5uVo/rIrHH3mxKctN/nRTFg+3j/dv6UQhmkUot/ldcUGhals1StraH4ZT - d+zzy/3PNTw35adqCtG+7PZf5s/rbnc+s62V9AdMa81+O3o2OZjFldBoUoO0lbFgO2dkKtWqAUvq - 83ENs+BZzD4d9C4NHHSz7RlofzSgVDPjbYpwu/eAuCdcKSuWy3lYdhf3vUCRVmFDVETRhSZRnbR6 - P+8Gg2w7g/cwplpKnTGyEmBs6Y/JGuAiKPKLwPV41rvLj5t0Ft5z9G+mCKcZunbMs8kO02KHfj+s - aEYkoGlVK0GPq5xFYUbmtM9muFOSZxpbkB8VkKNJEZlLaKBE2ZbHJAbMAnf+t/7Fh9kvvWAG+7JM - S0zB/w0TwoNLOdsilpwgAZNy03aWRcgTKuBbDdIAcY9XROMCGejgI9HJ2AwjaIG9w5cZROQKmo2w - QEFpenrb/xHOARNw8aml0TyNIPy/+qv+G5NozWlTBgAA - headers: - content-encoding: [gzip] - content-length: ['489'] - content-type: [application/json] - status: {code: 200, message: OK} -- request: - body: null - headers: {} - method: GET - uri: https://api.balancedpayments.com:443/customers/CU6X0aCm4qom2lpUvyxszYvv/cards?limit=10&offset=0 + uri: https://api.balancedpayments.com:443/customers/CU1LPZ5SkBmi1Spr6shPbNnT/cards?limit=10&offset=0 response: body: {string: !!python/unicode "{\n \"cards\": [],\n \"meta\": {\n \"last\": - \"/customers/CU6X0aCm4qom2lpUvyxszYvv/cards?limit=10&offset=0\",\n \"next\": - null,\n \"href\": \"/customers/CU6X0aCm4qom2lpUvyxszYvv/cards?limit=10&offset=0\",\n + \"/customers/CU1LPZ5SkBmi1Spr6shPbNnT/cards?limit=10&offset=0\",\n \"next\": + null,\n \"href\": \"/customers/CU1LPZ5SkBmi1Spr6shPbNnT/cards?limit=10&offset=0\",\n \ \"limit\": 10,\n \"offset\": 0,\n \"previous\": null,\n \"total\": - 0,\n \"first\": \"/customers/CU6X0aCm4qom2lpUvyxszYvv/cards?limit=10&offset=0\"\n + 0,\n \"first\": \"/customers/CU1LPZ5SkBmi1Spr6shPbNnT/cards?limit=10&offset=0\"\n \ },\n \"links\": {}\n}"} headers: content-length: ['364'] content-type: [application/json] status: {code: 200, message: OK} -- request: - body: null - headers: {} - method: GET - uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd - response: - body: - string: !!binary | - H4sIAAAAAAAAA41Uy27bMBC85ysEnWvLipQ29i1oby2CAG3RokUh0CQLEZFIhY+ggqF/79KWZJpU - wlx02J0ZcmdHPFwlSYqN0qKlUqW75DcUkuRw/EKLo5ZClZumeTfVGsYfLXQCAUwJI7EPhDqhSjOO - NBN8VBlFhlnNdARpSiqkAZFeb/Jytfmw2uTf8ptdWezKcp0Xxfvb7a90pmBJX6UU622+uSlKh0LE - vmoF17U/DCP22I/fi88/Sf98//UT7b88FN3dD7mtn8j5zK4W3B8wrSX9a+nZ7GAWVwKjcY24rpRG - 2lgjUy5WLdK4Ph/XUo08i+mTheZp4KCdradI+qMhQiRV3qYw070HhD3BSun1cjkPy/bivhcg0glo - NBUWZKGJheFa9m43GGRvFNxDqWopdUrxqkFKl/6YtEWsCYrsInADnPXH5sdO6oT3HP31HOE0A9eO - eVbZYV7s2B/GFTlEjCSpatGQ4yqdKDhkRobMwZ2S7GjsEX+sED6aFJG5hAZKhO5ZTGLELHDdv/UF - H5xfesEM+k9TySEFbxsmhAeXsrZFLDlBAiZhqjOaRsgzKuBribhC2D5eEY0LZKADj4ThsRkm0AL7 - GV5m1ESuIOkECxSEJKe3/ZVwjpiAC08tieZpAsH/NVwN/wHQiK4AUwYAAA== - headers: - content-encoding: [gzip] - content-length: ['499'] - content-type: [application/json] - status: {code: 200, message: OK} - request: body: null headers: {} @@ -989,13 +817,13 @@ interactions: body: {string: !!python/unicode "{\n \"card_holds\": [\n {\n \"status\": \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"card\": \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \"debit\": null\n },\n \"updated_at\": - \"2014-07-28T12:44:21.775480Z\",\n \"created_at\": \"2014-07-28T12:44:21.559915Z\",\n - \ \"transaction_number\": \"HL101-260-3999\",\n \"expires_at\": \"2014-08-04T12:44:21.689475Z\",\n + \"2014-08-06T16:10:49.329099Z\",\n \"created_at\": \"2014-08-06T16:10:49.118601Z\",\n + \ \"transaction_number\": \"HL656-773-9031\",\n \"expires_at\": \"2014-08-13T16:10:49.239247Z\",\n \ \"failure_reason\": null,\n \"currency\": \"USD\",\n \"amount\": 1000,\n \"meta\": {\n \"state\": \"new\",\n \"participant_id\": - \"2\"\n },\n \"href\": \"/card_holds/HL6ZwcjcM9CPa5eZEgQFeQ0g\",\n + \"2\"\n },\n \"href\": \"/card_holds/HL1NnMRbDzyqPSlCLkibOXSw\",\n \ \"failure_reason_code\": null,\n \"voided_at\": null,\n \"id\": - \"HL6ZwcjcM9CPa5eZEgQFeQ0g\"\n }\n ],\n \"links\": {\n \"card_holds.events\": + \"HL1NnMRbDzyqPSlCLkibOXSw\"\n }\n ],\n \"links\": {\n \"card_holds.events\": \"/card_holds/{card_holds.id}/events\",\n \"card_holds.card\": \"/cards/{card_holds.card}\",\n \ \"card_holds.debits\": \"/card_holds/{card_holds.id}/debits\",\n \"card_holds.debit\": \"/debits/{card_holds.debit}\"\n }\n}"} @@ -1004,26 +832,26 @@ interactions: content-type: [application/json] status: {code: 201, message: CREATED} - request: - body: '{"status": "succeeded", "transaction_number": "HL101-260-3999", "description": - "janet", "amount": 1000, "created_at": "2014-07-28T12:44:21.559915Z", "card_href": - "/cards/CC3MHBsW6vHaL3suLVdTVbO9", "updated_at": "2014-07-28T12:44:21.775480Z", - "expires_at": "2014-08-04T12:44:21.689475Z", "failure_reason": null, "currency": + body: '{"status": "succeeded", "transaction_number": "HL656-773-9031", "description": + "janet", "amount": 1000, "created_at": "2014-08-06T16:10:49.118601Z", "card_href": + "/cards/CC3MHBsW6vHaL3suLVdTVbO9", "updated_at": "2014-08-06T16:10:49.329099Z", + "expires_at": "2014-08-13T16:10:49.239247Z", "failure_reason": null, "currency": "USD", "is_void": true, "meta": {"state": "cancelled", "participant_id": "2"}, - "debit": null, "failure_reason_code": null, "voided_at": null, "id": "HL6ZwcjcM9CPa5eZEgQFeQ0g"}' + "debit": null, "failure_reason_code": null, "voided_at": null, "id": "HL1NnMRbDzyqPSlCLkibOXSw"}' headers: {} method: PUT - uri: https://api.balancedpayments.com:443/card_holds/HL6ZwcjcM9CPa5eZEgQFeQ0g + uri: https://api.balancedpayments.com:443/card_holds/HL1NnMRbDzyqPSlCLkibOXSw response: body: {string: !!python/unicode "{\n \"card_holds\": [\n {\n \"status\": \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"card\": \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \"debit\": null\n },\n \"updated_at\": - \"2014-07-28T12:44:22.144977Z\",\n \"created_at\": \"2014-07-28T12:44:21.559915Z\",\n - \ \"transaction_number\": \"HL101-260-3999\",\n \"expires_at\": \"2014-08-04T12:44:21.689475Z\",\n + \"2014-08-06T16:10:49.892521Z\",\n \"created_at\": \"2014-08-06T16:10:49.118601Z\",\n + \ \"transaction_number\": \"HL656-773-9031\",\n \"expires_at\": \"2014-08-13T16:10:49.239247Z\",\n \ \"failure_reason\": null,\n \"currency\": \"USD\",\n \"amount\": 1000,\n \"meta\": {\n \"state\": \"cancelled\",\n \"participant_id\": - \"2\"\n },\n \"href\": \"/card_holds/HL6ZwcjcM9CPa5eZEgQFeQ0g\",\n - \ \"failure_reason_code\": null,\n \"voided_at\": \"2014-07-28T12:44:22.144979Z\",\n - \ \"id\": \"HL6ZwcjcM9CPa5eZEgQFeQ0g\"\n }\n ],\n \"links\": {\n + \"2\"\n },\n \"href\": \"/card_holds/HL1NnMRbDzyqPSlCLkibOXSw\",\n + \ \"failure_reason_code\": null,\n \"voided_at\": \"2014-08-06T16:10:49.892523Z\",\n + \ \"id\": \"HL1NnMRbDzyqPSlCLkibOXSw\"\n }\n ],\n \"links\": {\n \ \"card_holds.events\": \"/card_holds/{card_holds.id}/events\",\n \"card_holds.card\": \"/cards/{card_holds.card}\",\n \"card_holds.debits\": \"/card_holds/{card_holds.id}/debits\",\n \ \"card_holds.debit\": \"/debits/{card_holds.debit}\"\n }\n}"} diff --git a/tests/py/fixtures/TestClosing.yml b/tests/py/fixtures/TestClosing.yml index 81665809eb..1a84534319 100644 --- a/tests/py/fixtures/TestClosing.yml +++ b/tests/py/fixtures/TestClosing.yml @@ -1,17 +1,17 @@ interactions: - request: - body: '{"meta": {"seq": 6}}' + body: '{"meta": {"seq": 7}}' headers: {} method: POST uri: https://api.balancedpayments.com:443/customers response: body: {string: !!python/unicode "{\n \"customers\": [\n {\n \"name\": null,\n \"links\": {\n \"source\": null,\n \"destination\": - null\n },\n \"updated_at\": \"2014-07-01T15:44:57.039650Z\",\n \"created_at\": - \"2014-07-01T15:44:56.925624Z\",\n \"dob_month\": null,\n \"id\": - \"CU5555ODX3LB3yvScKR27QPD\",\n \"phone\": null,\n \"href\": \"/customers/CU5555ODX3LB3yvScKR27QPD\",\n + null\n },\n \"updated_at\": \"2014-08-06T16:11:13.542466Z\",\n \"created_at\": + \"2014-08-06T16:11:13.412450Z\",\n \"dob_month\": null,\n \"id\": + \"CU2eHRmBFee1y5mc84bVX2JW\",\n \"phone\": null,\n \"href\": \"/customers/CU2eHRmBFee1y5mc84bVX2JW\",\n \ \"merchant_status\": \"no-match\",\n \"meta\": {\n \"seq\": - \"6\"\n },\n \"dob_year\": null,\n \"address\": {\n \"city\": + \"7\"\n },\n \"dob_year\": null,\n \"address\": {\n \"city\": null,\n \"line2\": null,\n \"line1\": null,\n \"state\": null,\n \"postal_code\": null,\n \"country_code\": null\n },\n \ \"business_name\": null,\n \"ssn_last4\": null,\n \"email\": @@ -41,13 +41,13 @@ interactions: \"321174851\",\n \"bank_name\": \"SAN MATEO CREDIT UNION\",\n \"account_type\": \"checking\",\n \"name\": \"Alice G. Krebs\",\n \"links\": {\n \"customer\": null,\n \"bank_account_verification\": null\n },\n \"can_credit\": - true,\n \"created_at\": \"2014-07-01T15:44:57.851974Z\",\n \"fingerprint\": + true,\n \"created_at\": \"2014-08-06T16:11:14.584220Z\",\n \"fingerprint\": \"5f0ba9fa3f1122ef13b944a40abfe44e7eba9e16934e64200913cb4c402ace14\",\n \"updated_at\": - \"2014-07-01T15:44:57.851976Z\",\n \"href\": \"/bank_accounts/BA567z1OsbkUtySKaCLl1QdX\",\n + \"2014-08-06T16:11:14.584222Z\",\n \"href\": \"/bank_accounts/BA2g1fNaSsXFegYbFlUMItH7\",\n \ \"meta\": {},\n \"account_number\": \"xxxxxx0001\",\n \"address\": {\n \"city\": null,\n \"line2\": null,\n \"line1\": null,\n \ \"state\": null,\n \"postal_code\": null,\n \"country_code\": - null\n },\n \"can_debit\": false,\n \"id\": \"BA567z1OsbkUtySKaCLl1QdX\"\n + null\n },\n \"can_debit\": false,\n \"id\": \"BA2g1fNaSsXFegYbFlUMItH7\"\n \ }\n ],\n \"links\": {\n \"bank_accounts.credits\": \"/bank_accounts/{bank_accounts.id}/credits\",\n \ \"bank_accounts.bank_account_verifications\": \"/bank_accounts/{bank_accounts.id}/verifications\",\n \ \"bank_accounts.customer\": \"/customers/{bank_accounts.customer}\",\n @@ -64,27 +64,27 @@ interactions: - request: body: '{"routing_number": "321174851", "bank_name": "SAN MATEO CREDIT UNION", "account_type": "checking", "name": "Alice G. Krebs", "links": {"customer": - "/customers/CU5555ODX3LB3yvScKR27QPD"}, "can_credit": true, "created_at": "2014-07-01T15:44:57.851974Z", - "fingerprint": "5f0ba9fa3f1122ef13b944a40abfe44e7eba9e16934e64200913cb4c402ace14", - "updated_at": "2014-07-01T15:44:57.851976Z", "meta": {}, "account_number": "xxxxxx0001", + "/customers/CU2eHRmBFee1y5mc84bVX2JW"}, "can_credit": true, "created_at": "2014-08-06T16:11:14.584220Z", "address": {"city": null, "line2": null, "line1": null, "state": null, "postal_code": - null, "country_code": null}, "can_debit": false, "id": "BA567z1OsbkUtySKaCLl1QdX"}' + null, "country_code": null}, "updated_at": "2014-08-06T16:11:14.584222Z", "customer": + null, "meta": {}, "bank_account_verification": null, "fingerprint": "5f0ba9fa3f1122ef13b944a40abfe44e7eba9e16934e64200913cb4c402ace14", + "can_debit": false, "id": "BA2g1fNaSsXFegYbFlUMItH7", "account_number": "xxxxxx0001"}' headers: {} method: PUT - uri: https://api.balancedpayments.com:443/bank_accounts/BA567z1OsbkUtySKaCLl1QdX + uri: https://api.balancedpayments.com:443/bank_accounts/BA2g1fNaSsXFegYbFlUMItH7 response: body: string: !!binary | - H4sIAAAAAAAAA5VTTW+jMBC991cgzm1ig4GSW5pUqyrdZNskUrXVChkzbK0QExlTLY3472tIKISm - qy4HH/yG8fuY2V8YhhlSsQkoY2kuVGaOjGd9aRj7+tSwTHPFxe9A5NsQpMZN28LYI9cONi+borqH - oFuo8OV4bnwfr24XxuTxdnq3Mtbzu8W8LT6+FahiV9ezF2Ab/URb0XQaJ5yB8W1gzCSEWYsnXGwq - qg1JTZPlmUq3B4KTtaO/xfTJvr+xi9clmz1a3sOP6XuDnurgFSSPOaOKp0K3FXmSHJWV7xIZFQGT - EHGlK5TMoUUkUAVRQCvEtBAmV8i7QniFnREhI8cbaK98j/xsBcRaLsid5KL+x4lRSP2Y2jHGlgUx - tkOfEEoQDWMgBDzQMGDXtwm4xELIxzYLCSPIogwwaRvnu+ifXK4Hluu6yO1weZEQVySGJ4MwvBk7 - rveGF1m4WatiOaOT+wQ/RE/tW1tQtAqh9ahJtp2VP/WHEOoMC40iCVk/P66Ko/WNsTolHTRY56/x - x+tMae0fr3epBpKApdEZsB57WXTRs9FHENbJxzTJ2uh5VDn3qVV1p1KfvypNlZrO2J4u3uAwW5Ur - /ST2J8EMeFQOm+KDVb1O3fKT0f5i8+42NEvXJ9vZtmGzetmwx7QByuPM9JrUln6R07H2//TWXp6o - 6TP81KrS1IGVF+VfDeyr4SIFAAA= + H4sIAAAAAAAAA5VTW2+bMBR+769APK9gG5PbW9o1azY1ldpk6zZNyJhDaoVLZMy0KOK/zxAol6ZT + x4Mf/B2Ov8s5xwvDMH2W7DzGeZonKjNnxk99aRjH6tSwTHMlkq2X5LEPUuOmQzAe04mLzQ9NUdUj + YTGU+ON8ZdzN1zf3xvXDzcfl2tislvertrh+y1OHfVXPn4Hv9BNtRdNpHgkOxifL+CLBz1o8Esmu + pNqQ1DR5nqk0PhG83hC4fYivFgD44MZ8Qv2vT+Tzt5cGA9Xeb5AiFJwpkSa6bZJHUa2seJHIWeJx + CYFQukLJHFpEAlMQeKxETIIwvUSTSzRa49EM4xmmljuhhKAfrYBQywW5lyKp/nFD5LNpyJwQY0Ig + xI4/pZRRxPwQKIUxaBjwaOpQGFGC0BQ73KecIsI4YNo2zvfBP7m4Fhm5ztjtcHmWEJYk7N4g2Fdz + ssXhij1mTwvYfvcX0eZuqW7H7VsxKFaG0HrUJNvOyp/qQwh1hoUFgYRsmJ9Qh9r6xlidkg4ayPlr + /Po6U1r76+t9qoHI42lwBqzGXh666NnoA/Cr5EMWZW30Iiide9OqqlOhz1+lplJNZ2z7i2edZqt0 + ZZjEsReMJYLCbopPVg06dct7o/3O5t1taJZuSLazbXazepk9YNoART0zgyaVpe/kVNf+n97Ky56a + IcM3rSpMHVhxUfwFDbm4ryIFAAA= headers: content-encoding: [gzip] content-length: ['533'] @@ -94,69 +94,49 @@ interactions: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU5555ODX3LB3yvScKR27QPD + uri: https://api.balancedpayments.com:443/customers/CU2eHRmBFee1y5mc84bVX2JW/bank_accounts?limit=10&offset=0 response: body: string: !!binary | - H4sIAAAAAAAAA41UTY+bMBS8769AnJtACCTaHLt7a6V+S1WrCjm2K6wFO/XHqlHEf+9zAsSx6Xo5 - cHieGfvNG/t0lyQpNkqLjkqV7pKfUEiS0/kPSxx1FKrctO2bsdYy/mShIwhgShiJfSDUCVWacaSZ - 4IPKINJPauZAkKakRhoQaZGvykW+XeSrr6tqV5a7arvM1/ebKv+RThQs6YuUzfK+qDZF6VCI2Ned - 4Lrxm2HEbvvwrYLvw+P39fu36+PzF/zuc7H99PHxuuehEdxvMG0k/W3p2eRgFlcCo3GDuK6VRtpY - I1MuFh3SuLlu11GNPIvpHwvdpIGDtrcjRdJvDREiqfImhZk+ekCYE4yUFvPlVVi2B/e9AJGDgIW2 - xoLMLGJhuJZHdzVoZG8UnEOpei51SvG6RUqXfpu0Q6wNiuwmcD3s9cvmx3bqhPca/eUU4TQD1855 - VtlpGuyw3g8jcogYSVI3oiXnUTpRcMiM9JmDuyTZ0dgj/lQjfDYpInMLDZQI3bOYxICZ4bq39T8+ - OFd6xgz6V1PJIQWvayaEB4eytkUsuUACJmHqYDSNkCdUwNcScYWwfbwiGjfIQAceCcNjPYygGfYz - vMyojRxB0hEWKAhJLm/7C+EcMAEXnloSzdMIgvvV3/X/AG22s3RTBgAA - headers: - content-encoding: [gzip] - content-length: ['498'] - content-type: [application/json] - status: {code: 200, message: OK} -- request: - body: null - headers: {} - method: GET - uri: https://api.balancedpayments.com:443/customers/CU5555ODX3LB3yvScKR27QPD/bank_accounts?limit=10&offset=0 - response: - body: - string: !!binary | - H4sIAAAAAAAAA61U22rbQBB9z1cIPfSpsXaltRQHQnEulJDUbmIHQksRq9WoWSxLZrUycYP+vbtr - KbokKSlEDwLNjGbOnHOYpwPLsiOarULKWF5msrCPrZ8qaFlP5q3SIi8lz36HWbmOQKi87bkYB+Ro - jO3PTZHpkdE16PxiOrO+TZcXc+vs9uL8cmndzS7ns7a4nhXK3cbUswdgKzWirWg6TVPOwPo6sq4E - REWbT3m20lAbkAomKwuZr/cAz+7G6pmf33vXp95uu2BXt25w8/38ucFg63ALgiecUcnzTLXNyjSt - N6ueV2Q0C5mAmEtVIUUJbUYAlRCHVGdsF2FyiIJDhJd4fEzI8TgYKa4mAfnRLpCodUFsBM/MP+ME - RXSSUC/B2HUhwV40IYQSRKMECIEAVBqwP/EI+MRFaII9FhFGkEsZYNI2LjfxP7EcjVzf95HfwfIg - INEgnJ4RnNPp2A/+4HkRre7kbnFFz65TfBPft7PWIKkWoeWoUbb1yqN5EEIds9A4FlAM9eNyV1Pf - EKtUUkKD+3oYvwwXUu3+MrzJVSINWR6/kjS2F7tu9lXpY4iM8glNi1Z6Hmvm3qTKdKrU+5feyW4I - M2E7pYUR32msWzhvGbcvzZeUr7k8wehTniQFyBNUa2Jn8Khbav/uSbSfxf24IWa6moJRPWOPQkWa - wEbAluelVrgDReZKBf1f/VvCxccRoCg1NtSO6ZyG/nEbGRE1rKHZn3reH/G4curaGmw/3/3qHY93 - 9u7em+asDaB271nHIQOgTVXVOKCPc3+v3gmqKf6/jQ2ZvX2cAcY3yapsrdpB9RchlguzhgYAAA== + H4sIAAAAAAAAA61UXU/bMBR951dEedjTaG3X6ZeEpsJgsIki8bGxTVPkONdgkY/KcRBV1f8+201I + UujEJPoQqfde33vuOUd3ted5fsSyh5BxnpeZLvyp99sEPW/lviat8lLL7C7MyjQCZfL+gGA8ouMA + +x/rItcjYynY/NVs7p3Pro8vvKPL489n197N/Oxi3hRXs0K9XLh6fg/8wYxoKupOs0Ry8L70vG8K + oqLJJzJ7sFBrkAYmLwudpxuARzcETi/TwxMAvAxSPqbR91vy9cdzg62tw0dQUkjOtMwz0zYrk6Ta + bP28ImdZyBXEUpsKrUpoMgqYhjhkNuMThOk+Gu+j4TUeTjGeYtoLxpQQ9KtZQJh1QS2UzNybQKCI + TQQbCIwJAYEH0YRSRhGLBFAKIzBpwMPJgMKQEoQmeMAjyikijAOmTeNyEf8TS9Ajw2AwClpY7hUI + C6LfMUL/cEbusJizq+L2BO5+RifJzfmZPh01s1LQzIrQcFQr23jlyf0QQi2zsDhWUGzrJ/Wyor4m + 1qhkhAbyehi/DBfa7P4yvMhNIgl5Hr+SdLZXy3b2VeljiJzygiVFI72MLXM7qXKd1ub7x+7k14S5 + sJ+wwonfr61b9HcZtyvNp0SmUh9g9CEXogB9gCpN/AyebEvr3w2J/rO47zfETTdTMKpmbFCYSB1Y + KHiUeWkVbkHRuVHBvqueCanejwBDqbOhdUzrNHSPW8+JaGFtm33V8X5Pxut+VVuB7ebb/zrH4429 + 2/emPmtbUNv3rOWQLaB11bp2QBfn5l69EVRd/H8bOzI7+/S3MO4ka+1b1fbWfwGe3xfJhgYAAA== headers: content-encoding: [gzip] content-length: ['625'] content-type: [application/json] status: {code: 200, message: OK} - request: - body: '{"amount": 1000.00, "description": "alice"}' + body: '{"amount": 1000.00, "meta": {"exchange_id": 1, "participant_id": 1}, "description": + "alice"}' headers: {} method: POST - uri: https://api.balancedpayments.com:443/bank_accounts/BA567z1OsbkUtySKaCLl1QdX/credits + uri: https://api.balancedpayments.com:443/bank_accounts/BA2g1fNaSsXFegYbFlUMItH7/credits response: body: {string: !!python/unicode "{\n \"credits\": [\n {\n \"status\": \"pending\",\n \"description\": \"alice\",\n \"links\": {\n \"customer\": - \"CU5555ODX3LB3yvScKR27QPD\",\n \"destination\": \"BA567z1OsbkUtySKaCLl1QdX\",\n - \ \"order\": null\n },\n \"updated_at\": \"2014-07-01T15:45:00.125855Z\",\n - \ \"created_at\": \"2014-07-01T15:44:59.861937Z\",\n \"transaction_number\": - \"CR401-922-8833\",\n \"failure_reason\": null,\n \"currency\": + \"CU2eHRmBFee1y5mc84bVX2JW\",\n \"destination\": \"BA2g1fNaSsXFegYbFlUMItH7\",\n + \ \"order\": null\n },\n \"updated_at\": \"2014-08-06T16:11:16.609323Z\",\n + \ \"created_at\": \"2014-08-06T16:11:16.336908Z\",\n \"transaction_number\": + \"CR558-399-7492\",\n \"failure_reason\": null,\n \"currency\": \"USD\",\n \"amount\": 1000,\n \"failure_reason_code\": null,\n - \ \"meta\": {},\n \"href\": \"/credits/CR58nuI5mbLeeWVDYO4ewvQB\",\n - \ \"appears_on_statement_as\": \"example.com\",\n \"id\": \"CR58nuI5mbLeeWVDYO4ewvQB\"\n + \ \"meta\": {\n \"exchange_id\": \"1\",\n \"participant_id\": + \"1\"\n },\n \"href\": \"/credits/CR2hZpvNGVj8Z79divl41tE4\",\n + \ \"appears_on_statement_as\": \"example.com\",\n \"id\": \"CR2hZpvNGVj8Z79divl41tE4\"\n \ }\n ],\n \"links\": {\n \"credits.order\": \"/orders/{credits.order}\",\n \ \"credits.customer\": \"/customers/{credits.customer}\",\n \"credits.destination\": \"/resources/{credits.destination}\",\n \"credits.reversals\": \"/credits/{credits.id}/reversals\",\n \ \"credits.events\": \"/credits/{credits.id}/events\"\n }\n}"} headers: - content-length: ['953'] + content-length: ['1018'] content-type: [application/json] status: {code: 201, message: CREATED} version: 1 diff --git a/tests/py/fixtures/TestCredits.yml b/tests/py/fixtures/TestCredits.yml index 1a72d27541..f2a916c151 100644 --- a/tests/py/fixtures/TestCredits.yml +++ b/tests/py/fixtures/TestCredits.yml @@ -1,26 +1,4 @@ interactions: -- request: - body: null - headers: {} - method: GET - uri: https://api.balancedpayments.com:443/customers/CU3O2IKYocPZ5LA7GkZWpwKZ - response: - body: - string: !!binary | - H4sIAAAAAAAAA41UTY/TMBS876+IcqabpJu2orcVB4QWCQ4gRBGKXNsoVhM7+AOoqvx3ntMk69oB - 7yWH55mJ37zxu9wlSYqN0qKlUqX75BsUkuQyfOGIo5ZClZumeTXVGsZPFjqBAKaEkdgHQp1QpRlH - mgk+qowi/axmOoI0JRXSgEjXeVGu8t0qLz4Vm335sC+397tNmW/zQzpTsKQRynazLnavHQoRx6oV - XNd+M4zY3775/PBh/e7pq8AfD5v3j7u3p8OX7veTI9DVgvsNprWkPyw9mx3M4kpgNK4R15XSSBtr - ZMrFqkUa188ttlQjz2L6czAoDRy0vZ0pkn5riBBJlTcpzPTZA8KcYKR0vVwuwrK9uO8FiHQCDpoK - C7JwiIXhWp7d06CRo1FwD6WqpdQpxasGKV36bdIWsSYospvA9fCv7zY/tlMnvM/Rv58jnGbg2pBn - lV3mwY7n/Tgih4iRJFUtGjKM0omCQ2akzxzcNcmOxhHxU4XwYFJE5hYaKBF6ZDGJEbPAdV/rP3xw - nvSCGfSPppJDCl7WTAgPLmVti1hyhQRMwlRnNI2QZ1TA1xJxhbBdXhGNG2SgA0vC8FgPE2iB/Qs2 - M2oiV5B0ggUKQpLrbv9POEdMwIVVS6J5mkDwvvq7/i9xeE/EUwYAAA== - headers: - content-encoding: [gzip] - content-length: ['496'] - content-type: [application/json] - status: {code: 200, message: OK} - request: body: null headers: {} @@ -46,7 +24,8 @@ interactions: content-type: [application/json] status: {code: 200, message: OK} - request: - body: '{"amount": 1900.00, "description": "bob"}' + body: '{"amount": 1900.00, "meta": {"exchange_id": 2, "participant_id": 4}, "description": + "bob"}' headers: {} method: POST uri: https://api.balancedpayments.com:443/bank_accounts/BA3QLnsEGgrsjqfhXh43Pq4d/credits @@ -54,18 +33,19 @@ interactions: body: {string: !!python/unicode "{\n \"credits\": [\n {\n \"status\": \"succeeded\",\n \"description\": \"bob\",\n \"links\": {\n \"customer\": \"CU3O2IKYocPZ5LA7GkZWpwKZ\",\n \"destination\": \"BA3QLnsEGgrsjqfhXh43Pq4d\",\n - \ \"order\": null\n },\n \"updated_at\": \"2014-07-01T19:19:43.494105Z\",\n - \ \"created_at\": \"2014-07-01T19:19:43.018298Z\",\n \"transaction_number\": - \"CR807-645-5056\",\n \"failure_reason\": null,\n \"currency\": + \ \"order\": null\n },\n \"updated_at\": \"2014-08-06T16:10:29.977848Z\",\n + \ \"created_at\": \"2014-08-06T16:10:29.680080Z\",\n \"transaction_number\": + \"CR130-017-0655\",\n \"failure_reason\": null,\n \"currency\": \"USD\",\n \"amount\": 1900,\n \"failure_reason_code\": null,\n - \ \"meta\": {},\n \"href\": \"/credits/CR56p7y5a2SbEJCkNqZUWXS9\",\n - \ \"appears_on_statement_as\": \"example.com\",\n \"id\": \"CR56p7y5a2SbEJCkNqZUWXS9\"\n + \ \"meta\": {\n \"exchange_id\": \"2\",\n \"participant_id\": + \"4\"\n },\n \"href\": \"/credits/CR1rvyY2Z7d46MsU9PScdBfT\",\n + \ \"appears_on_statement_as\": \"example.com\",\n \"id\": \"CR1rvyY2Z7d46MsU9PScdBfT\"\n \ }\n ],\n \"links\": {\n \"credits.order\": \"/orders/{credits.order}\",\n \ \"credits.customer\": \"/customers/{credits.customer}\",\n \"credits.destination\": \"/resources/{credits.destination}\",\n \"credits.reversals\": \"/credits/{credits.id}/reversals\",\n \ \"credits.events\": \"/credits/{credits.id}/events\"\n }\n}"} headers: - content-length: ['953'] + content-length: ['1018'] content-type: [application/json] status: {code: 201, message: CREATED} version: 1 diff --git a/tests/py/fixtures/TestPayin.yml b/tests/py/fixtures/TestPayin.yml index 45b584e25f..e8467331f5 100644 --- a/tests/py/fixtures/TestPayin.yml +++ b/tests/py/fixtures/TestPayin.yml @@ -1,26 +1,4 @@ interactions: -- request: - body: null - headers: {} - method: GET - uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd - response: - body: - string: !!binary | - H4sIAAAAAAAAA41Uy27bMBC85ysEnWvLipQ29i1oby2CAG3RokUh0CQLEZFIhY+ggqF/79KWZJpU - wlx02J0ZcmdHPFwlSYqN0qKlUqW75DcUkuRw/EKLo5ZClZumeTfVGsYfLXQCAUwJI7EPhDqhSjOO - NBN8VBlFhlnNdARpSiqkAZFeb/Jytfmw2uTf8ptdWezKcp0Xxfvb7a90pmBJX6UU622+uSlKh0LE - vmoF17U/DCP22I/fi88/Sf98//UT7b88FN3dD7mtn8j5zK4W3B8wrSX9a+nZ7GAWVwKjcY24rpRG - 2lgjUy5WLdK4Ph/XUo08i+mTheZp4KCdradI+qMhQiRV3qYw070HhD3BSun1cjkPy/bivhcg0glo - NBUWZKGJheFa9m43GGRvFNxDqWopdUrxqkFKl/6YtEWsCYrsInADnPXH5sdO6oT3HP31HOE0A9eO - eVbZYV7s2B/GFTlEjCSpatGQ4yqdKDhkRobMwZ2S7GjsEX+sED6aFJG5hAZKhO5ZTGLELHDdv/UF - H5xfesEM+k9TySEFbxsmhAeXsrZFLDlBAiZhqjOaRsgzKuBribhC2D5eEY0LZKADj4ThsRkm0AL7 - GV5m1ESuIOkECxSEJKe3/ZVwjpiAC08tieZpAsH/NVwN/wHQiK4AUwYAAA== - headers: - content-encoding: [gzip] - content-length: ['499'] - content-type: [application/json] - status: {code: 200, message: OK} - request: body: null headers: {} @@ -58,13 +36,13 @@ interactions: body: {string: !!python/unicode "{\n \"card_holds\": [\n {\n \"status\": \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"card\": \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \"debit\": null\n },\n \"updated_at\": - \"2014-07-28T13:06:03.447969Z\",\n \"created_at\": \"2014-07-28T13:06:02.929188Z\",\n - \ \"transaction_number\": \"HL204-775-3704\",\n \"expires_at\": \"2014-08-04T13:06:03.363939Z\",\n + \"2014-08-06T16:11:01.762199Z\",\n \"created_at\": \"2014-08-06T16:11:01.543142Z\",\n + \ \"transaction_number\": \"HL899-991-9495\",\n \"expires_at\": \"2014-08-13T16:11:01.671811Z\",\n \ \"failure_reason\": null,\n \"currency\": \"USD\",\n \"amount\": 1061,\n \"meta\": {\n \"state\": \"new\",\n \"participant_id\": - \"2\"\n },\n \"href\": \"/card_holds/HL7e0PQfQDeqDvm9LpSVYwEV\",\n + \"2\"\n },\n \"href\": \"/card_holds/HL21mbAoXnxrw1SuHknlouVa\",\n \ \"failure_reason_code\": null,\n \"voided_at\": null,\n \"id\": - \"HL7e0PQfQDeqDvm9LpSVYwEV\"\n }\n ],\n \"links\": {\n \"card_holds.events\": + \"HL21mbAoXnxrw1SuHknlouVa\"\n }\n ],\n \"links\": {\n \"card_holds.events\": \"/card_holds/{card_holds.id}/events\",\n \"card_holds.card\": \"/cards/{card_holds.card}\",\n \ \"card_holds.debits\": \"/card_holds/{card_holds.id}/debits\",\n \"card_holds.debit\": \"/debits/{card_holds.debit}\"\n }\n}"} @@ -73,26 +51,26 @@ interactions: content-type: [application/json] status: {code: 201, message: CREATED} - request: - body: '{"status": "succeeded", "transaction_number": "HL204-775-3704", "description": - "janet", "amount": 1061, "created_at": "2014-07-28T13:06:02.929188Z", "card_href": - "/cards/CC3MHBsW6vHaL3suLVdTVbO9", "updated_at": "2014-07-28T13:06:03.447969Z", - "expires_at": "2014-08-04T13:06:03.363939Z", "failure_reason": null, "currency": + body: '{"status": "succeeded", "transaction_number": "HL899-991-9495", "description": + "janet", "amount": 1061, "created_at": "2014-08-06T16:11:01.543142Z", "card_href": + "/cards/CC3MHBsW6vHaL3suLVdTVbO9", "updated_at": "2014-08-06T16:11:01.762199Z", + "expires_at": "2014-08-13T16:11:01.671811Z", "failure_reason": null, "currency": "USD", "is_void": true, "meta": {"state": "cancelled", "participant_id": "2"}, - "debit": null, "failure_reason_code": null, "voided_at": null, "id": "HL7e0PQfQDeqDvm9LpSVYwEV"}' + "debit": null, "failure_reason_code": null, "voided_at": null, "id": "HL21mbAoXnxrw1SuHknlouVa"}' headers: {} method: PUT - uri: https://api.balancedpayments.com:443/card_holds/HL7e0PQfQDeqDvm9LpSVYwEV + uri: https://api.balancedpayments.com:443/card_holds/HL21mbAoXnxrw1SuHknlouVa response: body: {string: !!python/unicode "{\n \"card_holds\": [\n {\n \"status\": \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"card\": \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \"debit\": null\n },\n \"updated_at\": - \"2014-07-28T13:06:03.805059Z\",\n \"created_at\": \"2014-07-28T13:06:02.929188Z\",\n - \ \"transaction_number\": \"HL204-775-3704\",\n \"expires_at\": \"2014-08-04T13:06:03.363939Z\",\n + \"2014-08-06T16:11:02.155199Z\",\n \"created_at\": \"2014-08-06T16:11:01.543142Z\",\n + \ \"transaction_number\": \"HL899-991-9495\",\n \"expires_at\": \"2014-08-13T16:11:01.671811Z\",\n \ \"failure_reason\": null,\n \"currency\": \"USD\",\n \"amount\": 1061,\n \"meta\": {\n \"state\": \"cancelled\",\n \"participant_id\": - \"2\"\n },\n \"href\": \"/card_holds/HL7e0PQfQDeqDvm9LpSVYwEV\",\n - \ \"failure_reason_code\": null,\n \"voided_at\": \"2014-07-28T13:06:03.805061Z\",\n - \ \"id\": \"HL7e0PQfQDeqDvm9LpSVYwEV\"\n }\n ],\n \"links\": {\n + \"2\"\n },\n \"href\": \"/card_holds/HL21mbAoXnxrw1SuHknlouVa\",\n + \ \"failure_reason_code\": null,\n \"voided_at\": \"2014-08-06T16:11:02.155202Z\",\n + \ \"id\": \"HL21mbAoXnxrw1SuHknlouVa\"\n }\n ],\n \"links\": {\n \ \"card_holds.events\": \"/card_holds/{card_holds.id}/events\",\n \"card_holds.card\": \"/cards/{card_holds.card}\",\n \"card_holds.debits\": \"/card_holds/{card_holds.id}/debits\",\n \ \"card_holds.debit\": \"/debits/{card_holds.debit}\"\n }\n}"} @@ -108,81 +86,134 @@ interactions: response: body: string: !!binary | - H4sIAAAAAAAAA92ZWU/bShTH3/spkB/6dG1mXyyhK7Y2gUBZ0lC4uooce0JcgpN4yQLiu3fsbI4a - C6uNkJKXCM2c4/+Z4aczM+e8ftrbM1wn9JqdXteLDHvvPz2yt/ea/eq5KHbiJB03osR1lfKUZ/wz - n/RU5IZ+P/Z7QWrx0wlUvJzt+sFT6jn/1kwptTw+xheVo+iODStODUdJreHVG61vcuGsbT3V8mNt - HCTd7kzwbaGc9D0nVl7TSS0MBCAxATeRqENsA2ojaXFKJZMPy3DcUL3jQrGEgORc4tAJIsdN19cM - kueWClO1So0TZhIMTSoJWQqocd8PVbQSkzABWcbEmARM5ATajt9NQtXUoUXZHqaLXSzSTcJQBe4k - Ff1+e7JUcp57SZCuHAEJF+bPKnZWtzv956nUO1Cj/Nb2nTD2Xb/vBHHT97IdNH7b4k6o2unU/hKP - /UqNHUdo+PQwcF7ugtP4hZ7e3l/GP5ahra6o6fa8NICVZQ17vqZouk0rE9NQCjWyCGcIbC+fyMbQ - xtjCjADOS/E5d4EEYUpL8Cmo5pNqPgllZficCyABgMzHtH18Xj91ru6/JBWVsOGZHJxWnujXi83i - uV5iN+gENhU2BRaFAmNUis65C9apjZWhk2Nq6kRrYp1uy9A5FyAYY57P6H9LJyZSfmz2hN5oNCLu - QIUt1/U61dbl2dFpY7N4FmjsEJ8QWpIKLMplzwwf7cIRELAMn0JCE1BkIg5BaT61gOBASrzB0/3j - +RwJeXdzXz0fdB4TR9LO/QiMJ5vFc73ErtAJhE2oxRlAK5mq+O4J7KkLZVhiUOJspzp7IqHvuoiU - pHMqwDjlFG41nbAdf629KIgdMBo7PwZXF+f8e+GK/uzuWaCxM3wiG3ILaTxJ2ewJtAuzJBdIsBJ8 - MgFMQqTJKRblsmcqwC2IkBDbnT3hQzIcEFE7m0wak5uTyVPMwqi62fRZoLH9fEJWh9wGLGWBIK4P - 4Xdvn3kXpC+HJI9P0dsdM2giJkwO2bLcYKx7u+sci5cx6a9Lni8O/O3t8+Pf7rh++jPut5LOZf36 - 8nF8fHVWTS5yBZtNvN0LNHaGT2ITbglE9GOkLJ+ZC9O4kTLnO6DUhFK/3zl/p7a04DMT4FjfbzeZ - Pz+eT/R4w65PTuS42lDfJKq3nVoSXG02fxZo7Ayf2Ma69qmLk+j92ucsf2YuFOlqab40WZQ/KSAm - xcjUObps/swEmC4pyO3OnxB1XPewen/TQucvI/e2dfTMk6PN8lmgsRt8MptqFvRLWVJG8iysfx+l - fM5dmICrz5ciPjkSJsRC1z9Bqfw5F+CSCJm/4G7f+U4Pz/0+aYzPv8Dx1c3hWej1EvyyWT4LNKZ8 - 6t//04KasdKKMLpOlLVpcp2Ef7v+sx8fIPo5NbWyRsWB7lJ87rXbkYoP5k9b3bkYz5tA01KdsaYv - UfprmWraN6Gzj03l9AiYDfRDNfR7Wcdr2Zsw4l7sdPUQnJu1/fAP1qS3JutfGKutsVwDzsq6Xlm/ - Ld93eV32YCzfe9ufWc1izk2mfy6co/28Xzr1NoPhN8XMZ/rVFadsaJ2XGqrg/ThnVum6P739AhEQ - I7VsHAAA + H4sIAAAAAAAAA92cW2/iWBLH3+dTtHiYp7X73C9Io1UISSCQhCQkJKxWkbHNJVzCxQRIq7/7lg0E + ewY3nm6LHXjoqIVd/I/hR9WpOnXOt9++fMnY1th5ab/1nEkm++U/8MqXL9+Cv3Bt4lne1H89M5na + tus6rpP51/qi407scWfodd4G/h29ludOvM3lXmfQ9U3Xb7aS8m89PWWVk271rn1/07rjzVsX31Ut + m9FPY7jXcRsdD24eTHu9leL3T+np0LE813mx/DsyBGFmIGlQVMU8S0WWCBMLgRipb4Zjj90fmnBT + E6FE2MQbW4OJZfsP+DKY9hvu2FcrlAkhhubUYFSKjYA7H3bG7iQyJmUgsRkTEgJHBJpWpzcduy8w + tEnwIfoP+/mQ9nQ8dgf2whd9uM9vlKz+23TgPzkVVHze3nc9K/px+9+e61sP3Fn4ox1aY69jd4bW + wHvpOMEnmPnLR9weu03/0tcNH1/h0Tnu1+z78WsLv0/ogHgl+7Fa3gwt+kQv9pvjDyDyWO9vHcBo + +TFFLiyHEqsRjHCFwE8B+moN3KR80qtCblIT7wWrTCfT8qNTfWzc6F/kkyifBU6zSJoYUaLQTj43 + JsJUmguiQiZxfHLBDYWAURL6SWVi+GSbMSEkMA4L/DKfTOs988nyp7TUf8Qno+YzHvbL7M6+uE6Z + z+0aR8MnBhdqgmuhFCflMzDBnDFOE/ApKTc4RQZmkuz0n0s+AwEipcAyJHB4fI5ORrP8g0Oe546T + yz2021zkLtLFc7vEsdDJRBYxUwjORdhTbY/uS++5NGGcKx2GJ857AmIGV9IglO+O7gGdSwEOLpfz + g6aT2B8FdvMxHxR7TxcTVj9HgylPO7pv1zgaPlmWEJNJhXVS78kCE8KZwOEJQRyfBAkDYWWAt03o + PZcCFKasNDy9PTzv2TutPrvkvN0q4qI9nl1dXJ92Y39wPzf53C5xNHTC3BObWjNJWcLYzgITSYTk + SbwnMGYoQQ2k0Sad++HccymgtCZExH6Zf04iEuRGe597igv5UCp/nA8fCwWHjG7m41tceEo3uMdo + HA2fMM9jJtYQrBN7TzChpsJEKZ1g7kmVMgTFBrjbTTr3Yz6XY0JKicOO7pw3JyX5/JTr5Au4V310 + m/nzEkqXzxiNY+ETiSymUFuSKOKrfjT79E2IqRljKAmfWhGI7gJyd5Rw9rkcE5IEoXDydXjRnX9Y + VfXkVlQ3X1XX7kXHxeotNiL8XHiP0TgOPiFSqyxnJkJI82TZ0dKEmpBZ04h7i5t9Cph5YgT/sAi5 + jvja0lpAIwbVq9hv8xDiO5kLmrev6vl8pTr6eK2Vbro1Mk/Xf8ZoHA2fkL0rUzIFuXWi+SfgE5hw + qrUOm8TxqQQsBQhkQHlVJqktrQWEIFilWfskSOP91j5l9zXvlShzxvXOqTUjjaeqfAqliGnU5mM0 + joZPniXalJxrEQ7W8fEd8AlMgE+MwilVHJ+SCVg3wgaHGUFCPgMBITSKVLx+Nb7vn09xOiHv3frI + +qgNzrwPfnb/fO2lnR9t1zgOPkmWBskOFQzJcDIez+faBDMoZ4bTlzg+FQc+OfAJ4ToJn2sBWMxC + kfLq4fF5221Xns+nBXcq3i/16KzQ5RdX6Yb37RLHQSfKcph9IpNjFa0zxtO5NqHg2hLNPv2VI3C0 + BgV3m4TOtQCDxSwZ9ui/Sifde3UJO7PZjNkjd9ywbaddbFxf5s4e08UzRuOI+MRQ/eSKqmTeM8AH + TCC3VjiR99TYQJwYROJE2dFaQEmk9WFn7zOla3fPxdKo3ZpamrefZ2i+SBfP7RLHQifk7oybUiAS + 8VQ/8p5LEy6opklWjjh4T+KvbBKWkM6lgF/6j9RjD9B7Nr2L8oeLqYVmc+tpVLkqyYfYasTP1Zbw + do2j4ZNksTQJ4MmSek8EJsLUUhGVJHcXChmMaUNyqpJFd18AeqkIFP8P23vi+vR9xFT5crF4XNzl + F11PjCfFdN1njMbh84mhx1L6hSJggRGpEqy8h00ITA5Zkr4lKmDdSChDYrFj7cjvHtmMCd5dy3Bx + 4Ff95/5zd1o9e/WGjWn7unp73ZqfVi6L06tQM2UataUYjaPhk2WZNBVhkIzsrH2u+AxMoGlYsyTx + HXFuYA35u5Q7akuffAYCksL8Nk3/uX8+SetO3Obzel58dG80qTat8nRQSdd/xmgcDZ80S6H2CcVJ + srv2ueIzMOEEqqWJ+pIRg75PYoCPTuo/AwEBJQV92P4Tk7ZtnxSf7xqk9DGz7xu5vpzm0uUzRuPQ + +VzuoYDgDskyM2EdiMhdffNREyyhoJlkX4eC2jxG2sCE7vCfsP4J8X09JqKVVIfdG4KrTOXReflU + Pl10G45YFJW+a6TM53aNfyifmVoe356wUrsmOqfd93r9rFWfWSXY8RMM+HMrCmxV2rYbKYwgMan0 + G+h2RP2wCTYJbC4SSaI+LKsbDDpGBOM7s/oNtRhWEfxF/9gc+O+vyO8/6uNKedFrjgqNcb01586s + UnsVXi1lardr/EOpTbxbbg0b0llEYfkS80j9cVvVKWoCc0bCk3hVBhUnqpghoMNkV1a/5HM5JuiY + 0ql2jOyfT3k+sTt1dDnM9xit3OX4oHDScNPlM0bjaPgM1pQUgSR9V8fIJ5+BiUCCRgpVcSueXAhY + jUeG4nxnx8iKz0AAsibK0pyV7n9NSY71MPdA6zXSvxo5LcYX3u1baIdvGll9jMah8wk+DVfBUXHs + b96AGSasYe6I71ETjiFgJ+moJ7DeSTA2sFA7sib4AcjNmASX0NOUYnzfP5/46kOP8HuODPuLijM6 + 61R7Zzehhd80+IzROBo+EWxPM6HCiRJ01K+QDkygX5NFGo7i/KcmGtbjCezmFHxXfF/xGQgQCjOC + NDvu/g98ykXLvSme1Bv0+vqyby9QzulV043veLvGkk/4+1+/yTAT2fyf6VlwEEN07/6/e51+x/uD + 8N/9W83gaIA/4FyA39+azYnrX1gNGw4LmP+88ZZTA3Ypr5OWTDBCGDbhy77JzHJo8ApavTAcu++d + t+BAis3JARnvzbN6vt1qr3mm2Rn/7edHfloXnC4A4wgfXBE6H8MMzqQIjsMIn4rwbXNCgtlxvn9d + 3bUac+ii/99P48nXsJ1/6fv6GwiZrE/ByKzeNWIUXNxm5b67g93jXN3lP/dv3/8HWZPLbAtEAAA= headers: content-encoding: [gzip] - content-length: ['1203'] + content-length: ['2393'] content-type: [application/json] status: {code: 200, message: OK} - request: - body: '{"status": "succeeded", "transaction_number": "HL728-138-4504", "description": - "janet", "amount": 2091, "created_at": "2014-07-16T16:53:31.681751Z", "card_href": - "/cards/CC3MHBsW6vHaL3suLVdTVbO9", "updated_at": "2014-07-16T16:53:31.895644Z", - "expires_at": "2014-07-23T16:53:31.794896Z", "failure_reason": null, "currency": - "USD", "is_void": true, "meta": {"state": "cancelled", "participant_id": "2"}, - "debit": null, "failure_reason_code": null, "voided_at": null, "id": "HL5AKip4VxKF1xPRAJrdou3z"}' + body: '{"status": "succeeded", "debit_href": "/debits/WD1QA4KhW6iCkvZZEgZwaK76", + "transaction_number": "HL562-416-6450", "description": "janet", "created_at": + "2014-08-06T16:10:51.237660Z", "card_href": "/cards/CC3MHBsW6vHaL3suLVdTVbO9", + "updated_at": "2014-08-06T16:10:52.377267Z", "expires_at": "2014-08-13T16:10:51.367763Z", + "failure_reason": null, "currency": "USD", "amount": 2091, "meta": {"state": + "captured", "participant_id": "2"}, "failure_reason_code": null, "voided_at": + null, "id": "HL1PLylfqHbrZgx5dwPWj6tW"}' headers: {} method: PUT - uri: https://api.balancedpayments.com:443/card_holds/HL5AKip4VxKF1xPRAJrdou3z + uri: https://api.balancedpayments.com:443/card_holds/HL1PLylfqHbrZgx5dwPWj6tW response: body: {string: !!python/unicode "{\n \"card_holds\": [\n {\n \"status\": \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"card\": - \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \"debit\": null\n },\n \"updated_at\": - \"2014-07-28T13:06:04.874888Z\",\n \"created_at\": \"2014-07-16T16:53:31.681751Z\",\n - \ \"transaction_number\": \"HL728-138-4504\",\n \"expires_at\": \"2014-07-23T16:53:31.794896Z\",\n - \ \"failure_reason\": null,\n \"currency\": \"USD\",\n \"amount\": - 2091,\n \"meta\": {\n \"state\": \"cancelled\",\n \"participant_id\": - \"2\"\n },\n \"href\": \"/card_holds/HL5AKip4VxKF1xPRAJrdou3z\",\n - \ \"failure_reason_code\": null,\n \"voided_at\": \"2014-07-28T13:06:04.874891Z\",\n - \ \"id\": \"HL5AKip4VxKF1xPRAJrdou3z\"\n }\n ],\n \"links\": {\n - \ \"card_holds.events\": \"/card_holds/{card_holds.id}/events\",\n \"card_holds.card\": - \"/cards/{card_holds.card}\",\n \"card_holds.debits\": \"/card_holds/{card_holds.id}/debits\",\n - \ \"card_holds.debit\": \"/debits/{card_holds.debit}\"\n }\n}"} + \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \"debit\": \"WD1QA4KhW6iCkvZZEgZwaK76\"\n + \ },\n \"updated_at\": \"2014-08-06T16:11:03.855360Z\",\n \"created_at\": + \"2014-08-06T16:10:51.237660Z\",\n \"transaction_number\": \"HL562-416-6450\",\n + \ \"expires_at\": \"2014-08-13T16:10:51.367763Z\",\n \"failure_reason\": + null,\n \"currency\": \"USD\",\n \"amount\": 2091,\n \"meta\": + {\n \"state\": \"captured\",\n \"participant_id\": \"2\"\n },\n + \ \"href\": \"/card_holds/HL1PLylfqHbrZgx5dwPWj6tW\",\n \"failure_reason_code\": + null,\n \"voided_at\": null,\n \"id\": \"HL1PLylfqHbrZgx5dwPWj6tW\"\n + \ }\n ],\n \"links\": {\n \"card_holds.events\": \"/card_holds/{card_holds.id}/events\",\n + \ \"card_holds.card\": \"/cards/{card_holds.card}\",\n \"card_holds.debits\": + \"/card_holds/{card_holds.id}/debits\",\n \"card_holds.debit\": \"/debits/{card_holds.debit}\"\n + \ }\n}"} headers: - content-length: ['975'] + content-length: ['971'] content-type: [application/json] status: {code: 200, message: OK} - request: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd + uri: https://api.balancedpayments.com:443/card_holds?limit=25&meta.state=new&offset=25 response: body: string: !!binary | - H4sIAAAAAAAAA41Uy27bMBC85ysEnWvLipQ29i1oby2CAG3RokUh0CQLEZFIhY+ggqF/79KWZJpU - wlx02J0ZcmdHPFwlSYqN0qKlUqW75DcUkuRw/EKLo5ZClZumeTfVGsYfLXQCAUwJI7EPhDqhSjOO - NBN8VBlFhlnNdARpSiqkAZFeb/Jytfmw2uTf8ptdWezKcp0Xxfvb7a90pmBJX6UU622+uSlKh0LE - vmoF17U/DCP22I/fi88/Sf98//UT7b88FN3dD7mtn8j5zK4W3B8wrSX9a+nZ7GAWVwKjcY24rpRG - 2lgjUy5WLdK4Ph/XUo08i+mTheZp4KCdradI+qMhQiRV3qYw070HhD3BSun1cjkPy/bivhcg0glo - NBUWZKGJheFa9m43GGRvFNxDqWopdUrxqkFKl/6YtEWsCYrsInADnPXH5sdO6oT3HP31HOE0A9eO - eVbZYV7s2B/GFTlEjCSpatGQ4yqdKDhkRobMwZ2S7GjsEX+sED6aFJG5hAZKhO5ZTGLELHDdv/UF - H5xfesEM+k9TySEFbxsmhAeXsrZFLDlBAiZhqjOaRsgzKuBribhC2D5eEY0LZKADj4ThsRkm0AL7 - GV5m1ESuIOkECxSEJKe3/ZVwjpiAC08tieZpAsH/NVwN/wHQiK4AUwYAAA== + H4sIAAAAAAAAA91WXW+bMBR976+IeOjTIIBtPiJVk9ZsTadMjdakkTpNkWub1S0BZCBtF+W/z3Yg + gNZ0qdSXLg8o4t7rc+7lyOeuj3o9g2BBF7dpTHNj0Psh3/R6a/2UsbzARaneG3lJCGOUUeNDHaQs + J4JnBU8TlXGHE1Y00Zgn96qyPqtCUpmnp+Db6FM+91YjPAZ5Ob6i06ubi3BXLHMpu+GFTE7KOK4A + NzvkMqO4YHSBVYbh2g40bd8EztQOB9AfQM9yfflD1w0dItiLJchyAhTaXqukEDjJMVH9LZJyecOE + QhuNfRuYoeea0PP8BoA9ZlywvMMpkLRqTsgKHRg4oAUQYR6Xgi0ktVzPUDW7a5KUQrCEPCnQ2eWw + QcLLtExU5wCG4S59yQrcHbf6eExVJ+yhPdoMi4ITnuGkWHCqJ2j8NeJbwSIV6jfy6I/GaEjIZ4ge + Vr/JuXs+KrP502Q+a6h1O1qQlCoCnbZWKZcq2o6pE9hS2YuhGVYSeNf6hAMbWQD6Lmhr4UV96hLb + t+3QOUCfbohMhAITObZ9oD41gBMi4MN3rU9nOAzuWUxmyfezdBRFs4trOhVvq889GP+HPoMBBAMA + LOBBxz1Mn3WJvN28zpW79/4MfVOmmhD5wSH6rAHknQ6c4F3rE13yyV08+fX1S5BP/TPgLE8ZTd5W + n3swtvqUz5/KMoyOXRgxzrWVtm77jzFf8uLERccq1dJmciKd5DiNopypQEVb2stj7dRbNzKeMY/D + j9O4koyLqtO2gO03mWArnm4Xk1dQrm9Do0gLHKsDgwoi4uLVA7CVaWpDMrq7TmujsvQaoxeotpGu + G1O1ON30q6yKTCuo/u6K8367ToU29SdoldSLk1Gd2inSweeq2Iol/+ZZZam+jzZ/ADCSHVk9CgAA headers: content-encoding: [gzip] - content-length: ['499'] + content-length: ['684'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: '{"status": "succeeded", "transaction_number": "HL797-867-4578", "description": + "janet", "amount": 3499, "created_at": "2014-07-31T08:43:33.148675Z", "card_href": + "/cards/CC3MHBsW6vHaL3suLVdTVbO9", "updated_at": "2014-07-31T08:43:33.364123Z", + "expires_at": "2014-08-07T08:43:33.277318Z", "failure_reason": null, "currency": + "USD", "is_void": true, "meta": {"state": "cancelled", "participant_id": "2"}, + "debit": null, "failure_reason_code": null, "voided_at": null, "id": "HL5SiPjlPgJF8sT7G31mCedn"}' + headers: {} + method: PUT + uri: https://api.balancedpayments.com:443/card_holds/HL5SiPjlPgJF8sT7G31mCedn + response: + body: {string: !!python/unicode "{\n \"card_holds\": [\n {\n \"status\": + \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"card\": + \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \"debit\": null\n },\n \"updated_at\": + \"2014-08-06T16:11:04.618331Z\",\n \"created_at\": \"2014-07-31T08:43:33.148675Z\",\n + \ \"transaction_number\": \"HL797-867-4578\",\n \"expires_at\": \"2014-08-07T08:43:33.277318Z\",\n + \ \"failure_reason\": null,\n \"currency\": \"USD\",\n \"amount\": + 3499,\n \"meta\": {\n \"state\": \"cancelled\",\n \"participant_id\": + \"2\"\n },\n \"href\": \"/card_holds/HL5SiPjlPgJF8sT7G31mCedn\",\n + \ \"failure_reason_code\": null,\n \"voided_at\": \"2014-08-06T16:11:04.618333Z\",\n + \ \"id\": \"HL5SiPjlPgJF8sT7G31mCedn\"\n }\n ],\n \"links\": {\n + \ \"card_holds.events\": \"/card_holds/{card_holds.id}/events\",\n \"card_holds.card\": + \"/cards/{card_holds.card}\",\n \"card_holds.debits\": \"/card_holds/{card_holds.id}/debits\",\n + \ \"card_holds.debit\": \"/debits/{card_holds.debit}\"\n }\n}"} + headers: + content-length: ['975'] content-type: [application/json] status: {code: 200, message: OK} - request: @@ -222,13 +253,13 @@ interactions: body: {string: !!python/unicode "{\n \"card_holds\": [\n {\n \"status\": \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"card\": \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \"debit\": null\n },\n \"updated_at\": - \"2014-07-28T13:06:06.523916Z\",\n \"created_at\": \"2014-07-28T13:06:06.298707Z\",\n - \ \"transaction_number\": \"HL055-552-8105\",\n \"expires_at\": \"2014-08-04T13:06:06.431593Z\",\n + \"2014-08-06T16:11:06.291378Z\",\n \"created_at\": \"2014-08-06T16:11:06.067053Z\",\n + \ \"transaction_number\": \"HL186-761-7704\",\n \"expires_at\": \"2014-08-13T16:11:06.204922Z\",\n \ \"failure_reason\": null,\n \"currency\": \"USD\",\n \"amount\": 4357,\n \"meta\": {\n \"state\": \"new\",\n \"participant_id\": - \"2\"\n },\n \"href\": \"/card_holds/HL7hNoqSlwMFeMD4jknZ37wh\",\n + \"2\"\n },\n \"href\": \"/card_holds/HL26rEm1tq2J5LBOmP1nEz0A\",\n \ \"failure_reason_code\": null,\n \"voided_at\": null,\n \"id\": - \"HL7hNoqSlwMFeMD4jknZ37wh\"\n }\n ],\n \"links\": {\n \"card_holds.events\": + \"HL26rEm1tq2J5LBOmP1nEz0A\"\n }\n ],\n \"links\": {\n \"card_holds.events\": \"/card_holds/{card_holds.id}/events\",\n \"card_holds.card\": \"/cards/{card_holds.card}\",\n \ \"card_holds.debits\": \"/card_holds/{card_holds.id}/debits\",\n \"card_holds.debit\": \"/debits/{card_holds.debit}\"\n }\n}"} @@ -237,26 +268,26 @@ interactions: content-type: [application/json] status: {code: 201, message: CREATED} - request: - body: '{"status": "succeeded", "transaction_number": "HL055-552-8105", "description": - "janet", "amount": 4357, "created_at": "2014-07-28T13:06:06.298707Z", "card_href": - "/cards/CC3MHBsW6vHaL3suLVdTVbO9", "updated_at": "2014-07-28T13:06:06.523916Z", - "expires_at": "2014-08-04T13:06:06.431593Z", "failure_reason": null, "currency": + body: '{"status": "succeeded", "transaction_number": "HL186-761-7704", "description": + "janet", "amount": 4357, "created_at": "2014-08-06T16:11:06.067053Z", "card_href": + "/cards/CC3MHBsW6vHaL3suLVdTVbO9", "updated_at": "2014-08-06T16:11:06.291378Z", + "expires_at": "2014-08-13T16:11:06.204922Z", "failure_reason": null, "currency": "USD", "is_void": true, "meta": {"state": "cancelled", "participant_id": "2"}, - "debit": null, "failure_reason_code": null, "voided_at": null, "id": "HL7hNoqSlwMFeMD4jknZ37wh"}' + "debit": null, "failure_reason_code": null, "voided_at": null, "id": "HL26rEm1tq2J5LBOmP1nEz0A"}' headers: {} method: PUT - uri: https://api.balancedpayments.com:443/card_holds/HL7hNoqSlwMFeMD4jknZ37wh + uri: https://api.balancedpayments.com:443/card_holds/HL26rEm1tq2J5LBOmP1nEz0A response: body: {string: !!python/unicode "{\n \"card_holds\": [\n {\n \"status\": \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"card\": \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \"debit\": null\n },\n \"updated_at\": - \"2014-07-28T13:06:06.900887Z\",\n \"created_at\": \"2014-07-28T13:06:06.298707Z\",\n - \ \"transaction_number\": \"HL055-552-8105\",\n \"expires_at\": \"2014-08-04T13:06:06.431593Z\",\n + \"2014-08-06T16:11:06.663119Z\",\n \"created_at\": \"2014-08-06T16:11:06.067053Z\",\n + \ \"transaction_number\": \"HL186-761-7704\",\n \"expires_at\": \"2014-08-13T16:11:06.204922Z\",\n \ \"failure_reason\": null,\n \"currency\": \"USD\",\n \"amount\": 4357,\n \"meta\": {\n \"state\": \"cancelled\",\n \"participant_id\": - \"2\"\n },\n \"href\": \"/card_holds/HL7hNoqSlwMFeMD4jknZ37wh\",\n - \ \"failure_reason_code\": null,\n \"voided_at\": \"2014-07-28T13:06:06.900890Z\",\n - \ \"id\": \"HL7hNoqSlwMFeMD4jknZ37wh\"\n }\n ],\n \"links\": {\n + \"2\"\n },\n \"href\": \"/card_holds/HL26rEm1tq2J5LBOmP1nEz0A\",\n + \ \"failure_reason_code\": null,\n \"voided_at\": \"2014-08-06T16:11:06.663121Z\",\n + \ \"id\": \"HL26rEm1tq2J5LBOmP1nEz0A\"\n }\n ],\n \"links\": {\n \ \"card_holds.events\": \"/card_holds/{card_holds.id}/events\",\n \"card_holds.card\": \"/cards/{card_holds.card}\",\n \"card_holds.debits\": \"/card_holds/{card_holds.id}/debits\",\n \ \"card_holds.debit\": \"/debits/{card_holds.debit}\"\n }\n}"} @@ -264,28 +295,6 @@ interactions: content-length: ['975'] content-type: [application/json] status: {code: 200, message: OK} -- request: - body: null - headers: {} - method: GET - uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd - response: - body: - string: !!binary | - H4sIAAAAAAAAA41Uy27bMBC85ysEnWvLipQ29i1oby2CAG3RokUh0CQLEZFIhY+ggqF/79KWZJpU - wlx02J0ZcmdHPFwlSYqN0qKlUqW75DcUkuRw/EKLo5ZClZumeTfVGsYfLXQCAUwJI7EPhDqhSjOO - NBN8VBlFhlnNdARpSiqkAZFeb/Jytfmw2uTf8ptdWezKcp0Xxfvb7a90pmBJX6UU622+uSlKh0LE - vmoF17U/DCP22I/fi88/Sf98//UT7b88FN3dD7mtn8j5zK4W3B8wrSX9a+nZ7GAWVwKjcY24rpRG - 2lgjUy5WLdK4Ph/XUo08i+mTheZp4KCdradI+qMhQiRV3qYw070HhD3BSun1cjkPy/bivhcg0glo - NBUWZKGJheFa9m43GGRvFNxDqWopdUrxqkFKl/6YtEWsCYrsInADnPXH5sdO6oT3HP31HOE0A9eO - eVbZYV7s2B/GFTlEjCSpatGQ4yqdKDhkRobMwZ2S7GjsEX+sED6aFJG5hAZKhO5ZTGLELHDdv/UF - H5xfesEM+k9TySEFbxsmhAeXsrZFLDlBAiZhqjOaRsgzKuBribhC2D5eEY0LZKADj4ThsRkm0AL7 - GV5m1ESuIOkECxSEJKe3/ZVwjpiAC08tieZpAsH/NVwN/wHQiK4AUwYAAA== - headers: - content-encoding: [gzip] - content-length: ['499'] - content-type: [application/json] - status: {code: 200, message: OK} - request: body: null headers: {} @@ -323,13 +332,13 @@ interactions: body: {string: !!python/unicode "{\n \"card_holds\": [\n {\n \"status\": \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"card\": \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \"debit\": null\n },\n \"updated_at\": - \"2014-07-28T13:06:08.748356Z\",\n \"created_at\": \"2014-07-28T13:06:08.539996Z\",\n - \ \"transaction_number\": \"HL864-060-0327\",\n \"expires_at\": \"2014-08-04T13:06:08.662188Z\",\n + \"2014-08-06T16:11:08.304615Z\",\n \"created_at\": \"2014-08-06T16:11:07.963391Z\",\n + \ \"transaction_number\": \"HL776-249-6275\",\n \"expires_at\": \"2014-08-13T16:11:08.220628Z\",\n \ \"failure_reason\": null,\n \"currency\": \"USD\",\n \"amount\": 2091,\n \"meta\": {\n \"state\": \"new\",\n \"participant_id\": - \"2\"\n },\n \"href\": \"/card_holds/HL7kjDtK34drZiCaw2bXT7X2\",\n + \"2\"\n },\n \"href\": \"/card_holds/HL28zFNdtqHnFRZsR5P5yYfj\",\n \ \"failure_reason_code\": null,\n \"voided_at\": null,\n \"id\": - \"HL7kjDtK34drZiCaw2bXT7X2\"\n }\n ],\n \"links\": {\n \"card_holds.events\": + \"HL28zFNdtqHnFRZsR5P5yYfj\"\n }\n ],\n \"links\": {\n \"card_holds.events\": \"/card_holds/{card_holds.id}/events\",\n \"card_holds.card\": \"/cards/{card_holds.card}\",\n \ \"card_holds.debits\": \"/card_holds/{card_holds.id}/debits\",\n \"card_holds.debit\": \"/debits/{card_holds.debit}\"\n }\n}"} @@ -345,30 +354,76 @@ interactions: response: body: string: !!binary | - H4sIAAAAAAAAA92ZWU/jSBDH3+dTID/M09r0fVhCq+XYSSAwHJlwrFaRY3eISXASHzlAfPdpO5ej - wcKaiZCSlwh1V/W/uvmp3F31+mVvz3Cd0Gt2+j0vMuy9//TI3t5r9qvnotiJk3TciBLXVcpTnvHX - YtJTkRv6g9jvB6nFkxOoeDXb84Nu6rlYa66UWh4d4fPKYXTLRhWnhqOk1vDqjdZ3uXTWtp5q+bE2 - DpJeby74tlROBp4TK6/ppBYGApCYgJtI1CG2AbOBsDgRmLKHVThuqD5woVhKmXeJQyeIHDfdXzNI - nlsqTNUqNcG0HAMmwIivBNRk4IcqWotJmICsYmIMQSFyMbUdv5eEqqlDi7IzTDe73KSbhKEK3Gkq - +uPmeKXkPPeTIN05AhIuzZ9V7Kwfd/rPU6l3oMb5ox04Yey7/sAJ4qbvZSdo/HLEnVC106n9FR77 - lRrvPh3HZ5h44YN/5IxR667O79AqtPUdNd2+lwawtq1R39cUzY5pbWIWSqFGFuEcga3mk9pIWpxS - yWRZPjMXzScEJOdSxCcnzCQYmlQSUpLPTIAxCdh288mOIjTqPgydl9vgJH6hJzf3F/HdZvks0NgN - PpGNoY2xhRkBnJfic+ECCcKUluBTUM0n1XwSysrwuRBAAgCZj2n78udVt3N5/29SUQkbncrhSaVL - v51vFs/3JXaDTmBTYVNgUSgwRqXoXLhgndpYGTo5pqZOtCbW6bYMnQsBgjHm+Yz+p3RiIuXnft2h - Nx6PiTtUYct1vU61dXF6eNLYLJ4FGjvEJ4SWpAKLctkzw0e7cAQELMOnkNAEFJmIQ1CaTy0gOJAS - b/D2+fl8joW8vb6vng07j4kjaed+DCbTzeL5vsSu0AmETajFGUBrmar4bQT0Wyp1oQxLDEp826nO - nkjotxgiJemcCTBOOYVbTSdsx99qLwpiB4wnzt3w8vyM/yjc0e+9jQo0doZPZENuIY0nKZs9gXZh - luQCiTJvdyaASYg0OcWiXPZMBbgFERJiu7MnfEhGQyJqp9NpY3p9PO3GLIyqm02fBRrbzydkdcjT - 2pJmgSCuP8If3j7zLkhfDkken6K3O2bQREyYHLJVOcx4r7akcyxexaRXlzxfHPjT2+fn15Zw/eQp - HrSSzkX96uJxcnR5Wk3OcwXFTdSWCjR2hk9iE24JRPRjpCyfmQvTuJEy33dAqQmlfr9z/kFtacln - JsCxvt9uMn9+Pp/o8ZpdHR/LSbWhvktUbzu1JLjcbP4s0NgZPrGNde1TFyfRx7XPef7MXCjS1dJ8 - abIof1JATIqRqXN02fyZCTBdUpDbnT8h6rjuP9X76xY6exm7N63DZ54cbpbPAo0Zn/r3/7RgYay1 - IoyeE2Vtmlwn4e+e/+zHB4h+TU2trFFxoLsUX/vtdqTig8XTQXcuJosm0KwUYrzTlyi9Wqaa9k3o - fLGZnB4B84FBqEZ+P+t4rXoTRtyPnZ4egguzth/+xp700WT9C2O9NZZrwFlZ1yvrt+X7Lq+rHozl - e2/7c6t5zLnJ9M+lc7Sf90un3uYw/KKY+cxWXXPKht7zUiMVfBzn3Crd95e3n/UFNSZsHAAA + H4sIAAAAAAAAA91c2W7qWBZ9r6+IeKintu+ZB6RSK4QkkJEQMpBWKzK2CYQhDCYMV/ffe9uGYFfh + 4M61IsFDUIS9Wcdmsfbo8/OPg4OcbY2c59Zb1xnn8gf/gXcODn4Gr3Bs7FnexH8/N57Ytus6rpP7 + 1+qg447tUXvgtd/6/hndF88de+vD3Xa/45uuPmwJ5Z96dMQqh51atXV7/VLlzRsXV2uWzeiHMZzr + uI22Byf3J93uEvHXB/Rk4Fie6zxb/hk5gjAzkDQoqmGepyJPhImFQIw8rZdjj9xPTbipiVAiauKN + rP7Ysv0LfO5Peg135KOVLgghhubUYFSKNYA7G7RH7ji2JmUgsV4TEgLHAJpWuzsZuc+wtHFwE/2L + /bhIezIauX177oPe3RbXSFbvbdL3r5wKKj5O77meFb/d/rfn+tZ9dxq9tQNr5LXt9sDqe89tJ7iD + uX/c4tbIbfqHfqz58QMunePeg307en3B72PaJ965fV+7WC8tfkXP9pvjLyB2We9vbaBReJtiB8Kl + JGIEK1xS4EsEfbX6blp+0stSYfwg3kvWBR1PLu6d2n3jWv8mP4nyucBpHkkTI0oU2srPtYkwleaC + qIhJEj+54IZCwFES+UnlEvjJ1mtCSGAcBfhtfjKtv5mfrHhEz3v3+HDYrONB74JV7dOrjPm5GWNv + +IlBQk2QFkpxWn4GJpgzxmkKfkrKDU6RgZkkW/Uz5GcAQKQUWEYAdo+fw8PhtHjnkPrMcQqFu1aL + i8JptvTcDLEv7GQij5gpBOciqlSbvXuonqEJ41zpKHmS1BMoZnAlDUL5du8esDME4CC5nO80O4m9 + KLHrxaxf7j6ejtnTCepPeNbefTPG3vCT5QkxmVRYp1VPFpgQzgSOBgRJ/CRIGAgrA9Q2pXqGABRC + VhoNb3dPPbtHtbpLTlovZVy2R9PL06ujTuIP7mvB52aIvWEnxJ7Y1JpJylL6dhaYSCIkT6OewDFD + CWogjdbp3KexZwigtCZEJH6Zf08iUuRG3x57ilN5d36xOBncl0oOGV7PRje49Jitc0/A2Bt+QpzH + TKzBWadWTzChpsJEKZ0i9qRKGYJiA+R2nc59zs9wTUgpsdvenfPm+FzWHwvtYgl3a/dus3hyjrLl + ZwLGvvATiTymUFuSKKZVn0WfvgkxNWMMpeGnVgS8u4DcHaWMPsM1IUkQiiZfu+fd+cKqqUe3ojrF + mrpyT9suVm+JHuFr7j0BYz/4CZ5a5TkzEUKap8uOQhNqQmZNY/KWFH0KiDwxgj8sItKRXFtaAWjE + oHqV+G3ugn8nM0GL9uVTsVipDRevD+fXnQcyy1Y/EzD2hp+QvStTMgW5dar4E+gTmHCqtY6aJPFT + CWgFCGRAeVWmqS2tAIQgWGVZ+yRI4++tfcrOa9E7p8wZPbWPrClpPNbkYyRFzKI2n4CxN/zkeaJN + ybkWUWed7N+BPoEJ8BOjaEqVxE/JBPSNsMEhIkjJzwBACI1iFa/f9e/fz09xNCbvnaehtXjoH3sL + fnxbv/Kyzo82Y+wHP0meBskOFQzJaDKezM+VCWZQzoymL0n8VBz4yYGf4K7T8HMFAM0sFCuv7h4/ + bzqtSv1kUnIn4v1MD49LHX56ma173wyxH+xEeQ7RJzI5VvE6YzI7VyYUpC1V9Ol3jkBoDQpym4ad + KwAGzSwZVfTfZSf99uoSdqbTKbOH7qhh206r3Lg6KxzfZ0vPBIw94ieG6idXVKVTz4A+YAK5tcKp + 1FNjA3FiEIlTZUcrACWR1rudvU+VfqjWy+fD1svE0rxVn6LZPFt6bobYF3ZC7s64KQUiMaX6TD1D + Ey6opmk6RxzUk/idTcJSsjME8Ev/sXrsDqpn0zu9WLiYWmg6sx6HlctzeZdYjfhabQlvxtgbfpI8 + liYBerK06onARJhaKqLS5O5CIYMxbUhOVTrv7gPALBWB4v9uqyd+mrwPmbo4m8/v59XivOOJ0bic + rXwmYOw+PzHMWEq/UARcYESqFJ33qAmB4JClmVuiAvpGQhkSiy29I396ZL0m+HQto8WB39XP78/d + ae341Rs0Jq2r2s3Vy+yoclaeXEaGKbOoLSVg7A0/WZ5JUxEGycjW2ueSn4EJDA1rlsa/I84NrCF/ + l3JLbemDnwGApBDfZqmf389P8lIVN8WinpXv3WtNak3rYtKvZKufCRh7w0+ap1D7hOIk2V77XPIz + MOEEqqWp5pIRg7lPYoBGp9XPAEBASUHvtn5i0rLtw3K92iDni6l92yj05KSQLT8TMHadn+EzFODc + sd8IogjG5KKp+Kb8KGoiTQ2jzLGQIKn2CU1Qg0D8KYjk2+JPDP59tSYCvfrYYP7u+XeiFidXjjcs + 9U+qT+Mqr/B5vfmaLT8TMPaGn1DMYSb0KYnc9lzHBz8DEyyh4J7muSMFvSOMtIEJ3eLfoT8f8jMA + IFpJtduzS7jGVBGdXBzJx9NOwxHzstLVRrb8TMDYF34inUcUGpWYxyqNn+lnaALRIeFp+MmgtkQV + MwTMkqTTzxAAZqN0prMh3x9/ypOx3X5CZ4Nil9FKtcD7pcOGmy0/EzD2hp9B90gRSMe3zYas9NMf + d0KmQILGSlJJ/p0LAX13ZCjOt86GhPoZAkB+RFmW8ef3d4/kSA8Kd/TpgfQuh84L43Pv5i3yLG8W + +XsCxq7zEzQN10CoOPYf0wBfDd3Krfl71IRjwVLNzhPobBKMDSzUlvwIfgByvSbBJUwvJVazd2G2 + Dl8u9BC/F8igN684w+N2rXt8HWnxZsHPBIy94SeCB9FMqGWiFLPzS34GJjCZyWKjRUn6qYmGzjuB + 5zbF1vxoyc8AgFCICLKcrft+/cRy/uJelw+fGvTq6qxnz1HB6day9e8JGCE/4fW//jhhLvaYf65r + wZYL8af0/91t99reX4T/6Z9qBpsA/AU7APz51myOXf/ActmwLcDs68Yb9gfYhrxqOuaCFcKyCQ8n + JHPh0uAdtHxjMHLf22/B1hPrPQJy3ptndX07tTyt2R7939eP/L0Ogn0EYB3RLSoiO2GYwe4TwcYX + 0f0Pfq73QjDbzq8fy7OWi4kc9P/9MB7/iNr5h36tvoGIyWq/i9zyU2NGwcFNVu6729++zuVZ/nX/ + 8et/Ggo6yvVDAAA= + headers: + content-encoding: [gzip] + content-length: ['2348'] + content-type: [application/json] + status: {code: 200, message: OK} +- request: + body: null + headers: {} + method: GET + uri: https://api.balancedpayments.com:443/card_holds?limit=25&meta.state=new&offset=25 + response: + body: + string: !!binary | + H4sIAAAAAAAAA91WXWvbMBR9768IeujT7MgfsqxAGawpS1lGxtq00DGCKslUq2Mb2U7blfz3SYoT + 26ztUuhLF4IJuvfqnHt9oqPHg8EAMKr44iZPeQlGgx96ZTB4tE8dKyta1WYdlDVjQnDBwYdtkIuS + KVlUMs9Mxi+aiaqNpjK7NZXbvRokk3l8HHydfCovo9WEToOynl7w84vrGdkV61wurmWlk7M6TRvA + 9Q65LjitBF9QkwF86IUOxE7gnUMyCvUXuhB7YRhctXSYEi+VBMTFAYow6ZRUimYlZaa/RVYvr4Uy + aJNpHPgOiiInwFFLGYj7QipR9jjFmlbDSQOQWCPADkBCZVorsdDUSjtD0+yuSVYrJTL2YEDnZ+O2 + FbrM68x0HoSE7NKXoqL9cZuXJ0x1Ju66oy2oqiSTBc2qheR2guCvEd8okZjQsJXHcDLFp/5tfjZF + Al6kEfuS3Z/M/HHSUut3tGA5NwR6ba1yqVW0GVMvsKHyLIZl2EjgXesTj8LI9bH+oH31qUuQ68WI + wGgPfWIYOCTynTCKcPtqXtSnBSBeGHvd/8z70ycaM3YSorvVb3bqn07q4vLh2+X8bfX5DMZ/o89w + BJEbhNgP9j4/bQnEEBJvD336BDkIxQ7yINxTnxbAIyjA4bs+P73xOL4VKZtn3z/nkySZz674uXpb + fT6DsdGnfv40lgF6dgFSWlor7Zz2H1O5lNWRjw5NqmvN5Eg7yWGeJKUwgYa2tpf7rVNv3Ag8YR77 + b2dxNRkfNbttALsrhRIrmW8uJq+gvFUbqPKKpmbDuIFIpHr1AKAxTWtIoH/X6dyoXHuNsReorpE+ + tqbqSr4eNlkNmU7Q/NwVl8NunQmtt6+gU7K9OIFm116RDT5VJVYi+zfPJsv0fbD+A55alHY9CgAA headers: content-encoding: [gzip] - content-length: ['1194'] + content-length: ['684'] content-type: [application/json] status: {code: 200, message: OK} version: 1 diff --git a/tests/py/fixtures/TestSyncWithBalanced.yml b/tests/py/fixtures/TestSyncWithBalanced.yml index 98f4cb64a5..afd3abaff3 100644 --- a/tests/py/fixtures/TestSyncWithBalanced.yml +++ b/tests/py/fixtures/TestSyncWithBalanced.yml @@ -1,26 +1,4 @@ interactions: -- request: - body: null - headers: {} - method: GET - uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd - response: - body: - string: !!binary | - H4sIAAAAAAAAA41Uy27bMBC85ysEnWvLipQ29i1oby2CAG3RokUh0CQLEZFIhY+ggqF/79KWZJpU - wlx02J0ZcmdHPFwlSYqN0qKlUqW75DcUkuRw/EKLo5ZClZumeTfVGsYfLXQCAUwJI7EPhDqhSjOO - NBN8VBlFhlnNdARpSiqkAZFeb/Jytfmw2uTf8ptdWezKcp0Xxfvb7a90pmBJX6UU622+uSlKh0LE - vmoF17U/DCP22I/fi88/Sf98//UT7b88FN3dD7mtn8j5zK4W3B8wrSX9a+nZ7GAWVwKjcY24rpRG - 2lgjUy5WLdK4Ph/XUo08i+mTheZp4KCdradI+qMhQiRV3qYw070HhD3BSun1cjkPy/bivhcg0glo - NBUWZKGJheFa9m43GGRvFNxDqWopdUrxqkFKl/6YtEWsCYrsInADnPXH5sdO6oT3HP31HOE0A9eO - eVbZYV7s2B/GFTlEjCSpatGQ4yqdKDhkRobMwZ2S7GjsEX+sED6aFJG5hAZKhO5ZTGLELHDdv/UF - H5xfesEM+k9TySEFbxsmhAeXsrZFLDlBAiZhqjOaRsgzKuBribhC2D5eEY0LZKADj4ThsRkm0AL7 - GV5m1ESuIOkECxSEJKe3/ZVwjpiAC08tieZpAsH/NVwN/wHQiK4AUwYAAA== - headers: - content-encoding: [gzip] - content-length: ['499'] - content-type: [application/json] - status: {code: 200, message: OK} - request: body: null headers: {} @@ -58,13 +36,13 @@ interactions: body: {string: !!python/unicode "{\n \"card_holds\": [\n {\n \"status\": \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"card\": \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \"debit\": null\n },\n \"updated_at\": - \"2014-07-31T09:51:18.956318Z\",\n \"created_at\": \"2014-07-31T09:51:18.731562Z\",\n - \ \"transaction_number\": \"HL595-244-2487\",\n \"expires_at\": \"2014-08-07T09:51:18.863860Z\",\n + \"2014-08-06T16:17:44.653585Z\",\n \"created_at\": \"2014-08-06T16:17:44.440285Z\",\n + \ \"transaction_number\": \"HL523-391-5751\",\n \"expires_at\": \"2014-08-13T16:17:44.565352Z\",\n \ \"failure_reason\": null,\n \"currency\": \"USD\",\n \"amount\": 2091,\n \"meta\": {\n \"state\": \"new\",\n \"participant_id\": - \"2\"\n },\n \"href\": \"/card_holds/HL1IjkJ7ps5zFIuHwAfD6E24\",\n + \"2\"\n },\n \"href\": \"/card_holds/HL1xrj6i5knWhOvt2SSAPCXS\",\n \ \"failure_reason_code\": null,\n \"voided_at\": null,\n \"id\": - \"HL1IjkJ7ps5zFIuHwAfD6E24\"\n }\n ],\n \"links\": {\n \"card_holds.events\": + \"HL1xrj6i5knWhOvt2SSAPCXS\"\n }\n ],\n \"links\": {\n \"card_holds.events\": \"/card_holds/{card_holds.id}/events\",\n \"card_holds.card\": \"/cards/{card_holds.card}\",\n \ \"card_holds.debits\": \"/card_holds/{card_holds.id}/debits\",\n \"card_holds.debit\": \"/debits/{card_holds.debit}\"\n }\n}"} @@ -77,18 +55,18 @@ interactions: "janet"}' headers: {} method: POST - uri: https://api.balancedpayments.com:443/card_holds/HL1IjkJ7ps5zFIuHwAfD6E24/debits + uri: https://api.balancedpayments.com:443/card_holds/HL1xrj6i5knWhOvt2SSAPCXS/debits response: body: {string: !!python/unicode "{\n \"debits\": [\n {\n \"status\": \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"customer\": \"CU3KXdyvNSDeyLP3pAWr9hqd\",\n \"source\": \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \ \"order\": null,\n \"dispute\": null\n },\n \"updated_at\": - \"2014-07-31T09:51:19.768970Z\",\n \"created_at\": \"2014-07-31T09:51:19.303392Z\",\n - \ \"transaction_number\": \"W741-253-2155\",\n \"failure_reason\": + \"2014-08-06T16:17:45.687922Z\",\n \"created_at\": \"2014-08-06T16:17:45.209792Z\",\n + \ \"transaction_number\": \"W946-597-2965\",\n \"failure_reason\": null,\n \"currency\": \"USD\",\n \"amount\": 1061,\n \"failure_reason_code\": null,\n \"meta\": {\n \"exchange_id\": \"1\",\n \"participant_id\": - \"2\"\n },\n \"href\": \"/debits/WD1IXiXjgMLtXAZSpgzrbB0d\",\n \"appears_on_statement_as\": - \"BAL*example.com\",\n \"id\": \"WD1IXiXjgMLtXAZSpgzrbB0d\"\n }\n + \"2\"\n },\n \"href\": \"/debits/WD1yiCx5knt1C67Mm97Rhyvj\",\n \"appears_on_statement_as\": + \"BAL*example.com\",\n \"id\": \"WD1yiCx5knt1C67Mm97Rhyvj\"\n }\n \ ],\n \"links\": {\n \"debits.customer\": \"/customers/{debits.customer}\",\n \ \"debits.dispute\": \"/disputes/{debits.dispute}\",\n \"debits.source\": \"/resources/{debits.source}\",\n \"debits.order\": \"/orders/{debits.order}\",\n @@ -106,20 +84,20 @@ interactions: response: body: string: !!binary | - H4sIAAAAAAAAA51U247aMBB9369AediHqkAuCzRIqOLysFXZthK7C9qqikw8gLeJk/qCoCj/Xtsx - JEFaVdqXyDPjmTPnzMSnm1bLSUEgZ9g6qbOyEsSFspwuhjUR/HNCUiJGfu9WX+vAId4huoWI4JF3 - m202HMTIdT6WuRQOOpfKJLGeHYPNe6sZZJXs92yxEk55XOvIGexJJnkTU2QCJcrl2Vsbwt5BSTEq - dAEnIfS3RrD6lLJ0YslFlgIz5M4G756uwsVZGuvHhOdSQClJea6SbPA6B/ZAhe7gPJMLCsFF10Yt - VwuTMWxbM6cKwpjXAGpGkuI3Ec7hJgTPJItLIgxKo4Ip7cK5iFj2pTj8NJtSaqnE5QIJMz+HyzgG - wICtYiqIgceM5IJkVN94RRREFW3OxexufSjTp+DrCh/33xYzOM5/BPl4ycLdn6q8Rr9wmE6Dh/sJ - X/b392gecDl/xo/P6+/hBU7dPotarbcBrUaqA4aeXR0TljlGAnCEzF/lu95d2x20A+/RDYc9b+iF - nUH/UzhwXypmMYP/pARuEIR+LUUwRDmKtVQRlem6nP5ycOe1/V7Q9r1er6q/QSSRDCIFw420dUZK - RMaAxkfd7tNiVqWhNJNUs/Dcvv23FL9msSjOsF6KRsXGA2NEqT0jGsary5wjJkhMckSFemWMaHqP - mqpevSvd5cz7siKr1+3DXKzGL4t8+5etJ25tmVCeA2I8UgLppYNU/VQRMqs3Gc8/wAGleQKdOEsr - xiX8m7VNV4X6/rop/gGWtguCSgUAAA== + H4sIAAAAAAAAA51U32vbMBB+718R/NCHsSS2Uzt1IIw2eSgs3cb6I2NjGEW6LGpt2dOPkBD8v0+S + ldgOlEFfjO5Od9993511uOj1vBwk8ia9gz5rK0NCassbElhRKT5lNKdyGkaX5toAdniD2B9IKZkG + l8V6LUBOfe9jnctgZ3KZyjLn2XBYv7eaRdbJYeSK1XDa4ztHyWFLCyW6mLKQKNOuwN1aU/4OSppR + ZQp4GWWvBsHpU8sywErIIgduyR0NMTychaujNM5PqCiVhFqS+twkueB5DmyBSdPBcSYnFEqqoYs6 + rg6m4MS1Zk8NhDXPAfSMFCNvIhzDXQhRKI5rIhxqo4Gp7co7iVj3pTn8sptSa6nFFRJJOz9PKIwB + CBCnmA4SEJjTUtKCmRsviIFsot252N1tD2X2NPr8g+y3Xx7msF98G5U3S55s/jblDfqJw2w2ur+7 + Fct4e4cWI6EWz+TxefU1OcHp20dRm/W2oM1ITcDSc6tjw6okSAJJkf2rQj+46vvXfT9+DOJJMJ5c + RYP4epyE4c+GGebwn5TQT3ROK0VyxATCRqqUqXxVT3+ZXMX9KBn3wySOmvprRDPFIdUwwkrbZqRF + 5BwY3pt2nx7mTRrKC8UMi8CP3b+l+XWLpbggZik6FTsPjBWl9YwYmKAtc4m4pJiWiEn9yljRzB51 + VT17V4bLebCns130ymQwi8f3eTL+vtlvX1rdlyUgLlItkFk6yPVPlSK7erc3iw+wQ3mZwQAXeZNT + w79Z23ZV6e/vi+ofRK3UuUoFAAA= headers: content-encoding: [gzip] - content-length: ['592'] + content-length: ['590'] content-type: [application/json] status: {code: 200, message: OK} - request: @@ -130,42 +108,20 @@ interactions: response: body: string: !!binary | - H4sIAAAAAAAAA51U247aMBB9369AediHqkAuCzRIqOLysFXZthK7C9qqikw8gLeJk/qCoCj/Xtsx - JEFaVdqXyDPjmTPnzMSnm1bLSUEgZ9g6qbOyEsSFspwuhjUR/HNCUiJGfu9WX+vAId4huoWI4JF3 - m202HMTIdT6WuRQOOpfKJLGeHYPNe6sZZJXs92yxEk55XOvIGexJJnkTU2QCJcrl2Vsbwt5BSTEq - dAEnIfS3RrD6lLJ0YslFlgIz5M4G756uwsVZGuvHhOdSQClJea6SbPA6B/ZAhe7gPJMLCsFF10Yt - VwuTMWxbM6cKwpjXAGpGkuI3Ec7hJgTPJItLIgxKo4Ip7cK5iFj2pTj8NJtSaqnE5QIJMz+HyzgG - wICtYiqIgceM5IJkVN94RRREFW3OxexufSjTp+DrCh/33xYzOM5/BPl4ycLdn6q8Rr9wmE6Dh/sJ - X/b392gecDl/xo/P6+/hBU7dPotarbcBrUaqA4aeXR0TljlGAnCEzF/lu95d2x20A+/RDYc9b+iF - nUH/UzhwXypmMYP/pARuEIR+LUUwRDmKtVQRlem6nP5ycOe1/V7Q9r1er6q/QSSRDCIFw420dUZK - RMaAxkfd7tNiVqWhNJNUs/Dcvv23FL9msSjOsF6KRsXGA2NEqT0jGsary5wjJkhMckSFemWMaHqP - mqpevSvd5cz7siKr1+3DXKzGL4t8+5etJ25tmVCeA2I8UgLppYNU/VQRMqs3Gc8/wAGleQKdOEsr - xiX8m7VNV4X6/rop/gGWtguCSgUAAA== - headers: - content-encoding: [gzip] - content-length: ['592'] - content-type: [application/json] - status: {code: 200, message: OK} -- request: - body: null - headers: {} - method: GET - uri: https://api.balancedpayments.com:443/customers/CU3KXdyvNSDeyLP3pAWr9hqd - response: - body: - string: !!binary | - H4sIAAAAAAAAA41Uy27bMBC85ysEnWvLipQ29i1oby2CAG3RokUh0CQLEZFIhY+ggqF/79KWZJpU - wlx02J0ZcmdHPFwlSYqN0qKlUqW75DcUkuRw/EKLo5ZClZumeTfVGsYfLXQCAUwJI7EPhDqhSjOO - NBN8VBlFhlnNdARpSiqkAZFeb/Jytfmw2uTf8ptdWezKcp0Xxfvb7a90pmBJX6UU622+uSlKh0LE - vmoF17U/DCP22I/fi88/Sf98//UT7b88FN3dD7mtn8j5zK4W3B8wrSX9a+nZ7GAWVwKjcY24rpRG - 2lgjUy5WLdK4Ph/XUo08i+mTheZp4KCdradI+qMhQiRV3qYw070HhD3BSun1cjkPy/bivhcg0glo - NBUWZKGJheFa9m43GGRvFNxDqWopdUrxqkFKl/6YtEWsCYrsInADnPXH5sdO6oT3HP31HOE0A9eO - eVbZYV7s2B/GFTlEjCSpatGQ4yqdKDhkRobMwZ2S7GjsEX+sED6aFJG5hAZKhO5ZTGLELHDdv/UF - H5xfesEM+k9TySEFbxsmhAeXsrZFLDlBAiZhqjOaRsgzKuBribhC2D5eEY0LZKADj4ThsRkm0AL7 - GV5m1ESuIOkECxSEJKe3/ZVwjpiAC08tieZpAsH/NVwN/wHQiK4AUwYAAA== + H4sIAAAAAAAAA51U32vbMBB+718R/NCHsSS2Uzt1IIw2eSgs3cb6I2NjGEW6LGpt2dOPkBD8v0+S + ldgOlEFfjO5Od9993511uOj1vBwk8ia9gz5rK0NCassbElhRKT5lNKdyGkaX5toAdniD2B9IKZkG + l8V6LUBOfe9jnctgZ3KZyjLn2XBYv7eaRdbJYeSK1XDa4ztHyWFLCyW6mLKQKNOuwN1aU/4OSppR + ZQp4GWWvBsHpU8sywErIIgduyR0NMTychaujNM5PqCiVhFqS+twkueB5DmyBSdPBcSYnFEqqoYs6 + rg6m4MS1Zk8NhDXPAfSMFCNvIhzDXQhRKI5rIhxqo4Gp7co7iVj3pTn8sptSa6nFFRJJOz9PKIwB + CBCnmA4SEJjTUtKCmRsviIFsot252N1tD2X2NPr8g+y3Xx7msF98G5U3S55s/jblDfqJw2w2ur+7 + Fct4e4cWI6EWz+TxefU1OcHp20dRm/W2oM1ITcDSc6tjw6okSAJJkf2rQj+46vvXfT9+DOJJMJ5c + RYP4epyE4c+GGebwn5TQT3ROK0VyxATCRqqUqXxVT3+ZXMX9KBn3wySOmvprRDPFIdUwwkrbZqRF + 5BwY3pt2nx7mTRrKC8UMi8CP3b+l+XWLpbggZik6FTsPjBWl9YwYmKAtc4m4pJiWiEn9yljRzB51 + VT17V4bLebCns130ymQwi8f3eTL+vtlvX1rdlyUgLlItkFk6yPVPlSK7erc3iw+wQ3mZwQAXeZNT + w79Z23ZV6e/vi+ofRK3UuUoFAAA= headers: content-encoding: [gzip] - content-length: ['499'] + content-length: ['590'] content-type: [application/json] status: {code: 200, message: OK} - request: @@ -205,13 +161,13 @@ interactions: body: {string: !!python/unicode "{\n \"card_holds\": [\n {\n \"status\": \"succeeded\",\n \"description\": \"janet\",\n \"links\": {\n \"card\": \"CC3MHBsW6vHaL3suLVdTVbO9\",\n \"debit\": null\n },\n \"updated_at\": - \"2014-07-31T09:51:22.745306Z\",\n \"created_at\": \"2014-07-31T09:51:22.516457Z\",\n - \ \"transaction_number\": \"HL210-211-1689\",\n \"expires_at\": \"2014-08-07T09:51:22.657621Z\",\n + \"2014-08-06T16:17:48.014432Z\",\n \"created_at\": \"2014-08-06T16:17:47.797164Z\",\n + \ \"transaction_number\": \"HL440-067-6287\",\n \"expires_at\": \"2014-08-13T16:17:47.917087Z\",\n \ \"failure_reason\": null,\n \"currency\": \"USD\",\n \"amount\": 3499,\n \"meta\": {\n \"state\": \"new\",\n \"participant_id\": - \"2\"\n },\n \"href\": \"/card_holds/HL1Mz9q1vB2pmyPdqEiTlEO1\",\n + \"2\"\n },\n \"href\": \"/card_holds/HL1Bdodkf2ZG8tmnnq5IyD3q\",\n \ \"failure_reason_code\": null,\n \"voided_at\": null,\n \"id\": - \"HL1Mz9q1vB2pmyPdqEiTlEO1\"\n }\n ],\n \"links\": {\n \"card_holds.events\": + \"HL1Bdodkf2ZG8tmnnq5IyD3q\"\n }\n ],\n \"links\": {\n \"card_holds.events\": \"/card_holds/{card_holds.id}/events\",\n \"card_holds.card\": \"/cards/{card_holds.card}\",\n \ \"card_holds.debits\": \"/card_holds/{card_holds.id}/debits\",\n \"card_holds.debit\": \"/debits/{card_holds.debit}\"\n }\n}"} @@ -303,48 +259,48 @@ interactions: response: body: string: !!binary | - H4sIAAAAAAAAA61UXW/aMBR9769AkdqHaUA+GlgqoYmPh06j2yTagjpNkbEv4C5xMttBMJT/Ptsx - hCBVe9heIl9f+5x7zr3x4arVclKQyLlrHdRaRQkSUkVOl8CSSvExoSmVAz+80ceuwxHOmESUietw - MoAd3iC2hpiSm2y1EiAHrvO+wmGw0zisSBK7s+Gw+h/IpiIF5IcWuKJWO67dyDlsaVaIJr/MJErU - lmdPrSj/R6lKaanBnISyn5rNelhZ18GFkFkK3Ig+BqJ7uEiXR8vsPqEiLyRUVlXr+pJNXt6BLTCp - Kzj27cRCSdm1Wavb0mSc2NLMqqYw4SWB6l3ByJsMx3STQmQFx5UQDlVQ01Rx6ZxMrOpSGr6bCaq8 - VOYKiaTppSMKjAEIEOuYShIQmNNc0ozpE6+Igayzzb6Y+T5vyvgp+Lwg++2X2QT2029BPpzzaPOr - htfsJw3jcfBwPxLz3vYeTQNRTJ/J4/Pya3SiU6ePptZjb0jrluqEkWdHx6SLnCAJJEbmz/Nd77bt - 9tuB9+hGd6F350Wdfu9D1HdfamWYw1+uBG4QRP7ZFckREwhrq2JWpMuq+/P+rdf2w6Dte2FY468Q - TQoOsaIRxtpzRcpEzoHhvS73aTapr6E0K5hW4bk9+58pfU2wGGdED0UDsfEIGVPOHhdN453bnCMu - KaY5YlK9PcY0PUdNVy/em+584n1a0MXr+mEqF8OXWb7+zZcj92yYUJ4D4iJWBumhg1T9VDEyozca - Tt/BDqV5Ah2cpbXiiv5NbFNVqb4/rso/4KyC3m4FAAA= + H4sIAAAAAAAAA61UTY/aMBC9769AkXYPVYEkkLBZCVULHFYq21bdD6pWVWTsoXg3cVJ/IBDiv9d2 + DEmQVj20l8gz45k3783E+4tOx8tBIu+ms9dnbWVISG15fQJLKsWHjOZUjsPoyly7jCa4YBJRJi6j + 2Ri2eI3YL0gpuSpWKwFy7HvvqzoMtqYOU1nmPGsOq/9R2XakC4WRK1xBa4/vHCWHDS2UaOPLQqJM + uwJ3a0X5P1LVTA+mmJdR9mrQnIaVdD2shCxy4Jb00RD9/Vn4cJTM+QkVpZJQSVWd6yQXPM+BDTBp + OjjO7YRCyaHvoo63gyk4ca3ZUw1hzXMAPTvFyJsIx3AbQhSK44oIh8qoYSr74J1ErPrSHH7YDaq0 + 1OIKiaSdpScUxgAEiFNMBwkIzGkpacHMjRfEQNbR9lzsfjeHMn0afPxGdptPDzPYzb8MytsFT9a/ + 6/IG/cRhOh3c303EIt7coflAqPkzeXxefk5OcPr2UdR67S1oPVITsPTc6tiwKgmSQFJk/7zQD4Zd + /7rrx49BfBOMboZRL74eJWH4vWaGOfwlJfQTndNIkRwxgbCRKmUqX1bTXyTDuBslo26YxFFdf4Vo + pjikGkZYaZuMtIicA8M70+7Tw6xOQ3mhmGER+LH7zzS/drEUF8QsRati6xGyojQeFwMTNGUuEZcU + 0xIxqd8eK5rZo7aqZ+9NfzELdnS6jV6ZDKbx6D5PRl/Xu81Lo/uyBMRFqgUySwe5/qlSZFdvcjt/ + B1uUlxn0cJHXORX8m7VtVwf9/Xlx+AMyt13lbgUAAA== headers: content-encoding: [gzip] - content-length: ['602'] + content-length: ['601'] content-type: [application/json] status: {code: 200, message: OK} - request: body: '{"status": "succeeded", "source_href": "/resources/CC3MHBsW6vHaL3suLVdTVbO9", - "transaction_number": "W741-253-2155", "description": "janet", "created_at": - "2014-07-31T09:51:19.303392Z", "appears_on_statement_as": "BAL*example.com", - "updated_at": "2014-07-31T09:51:19.768970Z", "order": null, "currency": "USD", + "transaction_number": "W946-597-2965", "description": "janet", "created_at": + "2014-08-06T16:17:45.209792Z", "appears_on_statement_as": "BAL*example.com", + "updated_at": "2014-08-06T16:17:45.687922Z", "order": null, "currency": "USD", "amount": 1061, "meta": {"participant_id": "2"}, "customer_href": "/customers/CU3KXdyvNSDeyLP3pAWr9hqd", - "failure_reason_code": null, "failure_reason": null, "id": "WD1IXiXjgMLtXAZSpgzrbB0d", + "failure_reason_code": null, "failure_reason": null, "id": "WD1yiCx5knt1C67Mm97Rhyvj", "dispute": null}' headers: {} method: PUT - uri: https://api.balancedpayments.com:443/debits/WD1IXiXjgMLtXAZSpgzrbB0d + uri: https://api.balancedpayments.com:443/debits/WD1yiCx5knt1C67Mm97Rhyvj response: body: string: !!binary | - H4sIAAAAAAAAA31Sy27bMBC85ysEHYvaEqXIrnyz40OKKm0BJ7GRohBocpMw1at8GHEN/XtJvai4 - SC+EyN2Z3RnN6cJxXAp7JoW7cH7om+OcmlO/C4mlMu+uUIQAUKDux75IQRDOKsnKwnS84AKkrWas - +GWQPZdmI0rIMgduuq/uwi87ejx83azhmHwPq+WWx8+/Lb2ZXipOoOm+Cm+uV2I7O1zjJBQquae3 - 9/tv8TBOd5ecNtSFyrJ+RyONiUpJw2IK3e71IEJVFEugKZZmTuCjy4k/n4To1o8XEVoE0XT2yUf+ - /MEqIxz+B0HxNPTDMA5GEMlxITAxVqWFyvetB9v5JZoEUTgJUBRZ/kfMMsUh1WNEY+1YkTaRcyjI - 0ax7t1lbGM5LVRgVyJ+hQd9bspSUtLdiaMlBYg0b/agKc8kIq3AhU0YbY9x/nHvm8GhKXhseb7tG - n3ds9/J0k8jd8mFTPf3h+5U/CgyuKsBcpNoEEyzIQfPjJl6rZfIBXnFeZTAlZW5VtePf5W62qvX5 - 08hx34aui/V0nDuvvwjv1C4+lOtuag+zydEa2xRZUPdwjrGR9Ti0F4tp7+eQPreu13zZ9uZ63q1N - VwVtPOuN73UwWuuhbbn9ub0SOGin38V0Ve1ffVH/BS/wjpIPBAAA + H4sIAAAAAAAAA31Sy27bMBC85ysMHYvakmzr5VtiHwLUaYvm4aJBIdDkBmYiUSofRgRD/x5SLyoO + 0gshcndmd0ZzuphMHAJ7KoWzmjzq22Ryak79LiSSyrw7QmEMQIA4X/siAYE5LSUtmOl4RgykrWaU + vRhkz6XZsBKyyIGb7vX94ttvUh2/326g2v5clJc7nhz+WXozvVAcQ9O9XtxcX4ldeLxG24VQ2wdy + 97D/kQzjdHfBSUPNVJb1OxppVJRKGhZT6HavBxGqJEgCSZE0c+aev5x68dQL7/xw5UerwJvFcRD7 + 8R+rDHP4H2QZzOZeEiXzEURyxATCxqqUqXzferBLluE0SKLpPAkDy/+EaKY4pHqMaKwdK9Imcg4M + V2bd+9uNhaG8UMyo8L3QH/S9J0txQXorhpYcJNKw0Y8qEZcU0xIxmVLSGON8cO7A4cmU3DY87m7j + V3T9Grww6a/D6CZPol+H6vg82rAsAXGRahNMsCAHzY+aeF1dbr/AK8rLDGa4yC2mHf8pd7NVrc+/ + Ro7zPnRdrGfj3Ln9RbindvGhXHdTe5hNjtbYpsiCuodzjI2sy6G9WEx7P4f0uXXc5su2N9fzbm26 + YqTxrDe+10FJrYe25fbn9krgqJ3+FNNVtX/1Rf0GIaIZfg8EAAA= headers: content-encoding: [gzip] - content-length: ['495'] + content-length: ['494'] content-type: [application/json] status: {code: 200, message: OK} version: 1 From 6e6cc519b09efbacdd9cee229b86aa8514e771f3 Mon Sep 17 00:00:00 2001 From: Changaco Date: Thu, 7 Aug 2014 09:59:36 +0200 Subject: [PATCH 77/97] add log message for debugging --- gittip/billing/payday.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gittip/billing/payday.py b/gittip/billing/payday.py index 93273f3007..243b3ceadd 100644 --- a/gittip/billing/payday.py +++ b/gittip/billing/payday.py @@ -469,7 +469,8 @@ def update_balances(cursor): # Check that balances aren't becoming (more) negative for p in participants: if p.new_balance < 0 and p.new_balance < p.cur_balance: - raise NegativeBalance(p) + log(p) + raise NegativeBalance() log("Updated the balances of %i participants." % len(participants)) From 207c7f958bc6bba3df2b59ea7e296ba54e9e36af Mon Sep 17 00:00:00 2001 From: Changaco Date: Thu, 7 Aug 2014 12:49:50 +0200 Subject: [PATCH 78/97] failing test for `transfer_tips()` --- tests/py/test_billing_payday.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/py/test_billing_payday.py b/tests/py/test_billing_payday.py index a166ca699b..a308f006d9 100644 --- a/tests/py/test_billing_payday.py +++ b/tests/py/test_billing_payday.py @@ -310,6 +310,19 @@ def test_payin_doesnt_make_null_transfers(self): transfers0 = self.db.all("SELECT * FROM transfers WHERE amount = 0") assert not transfers0 + def test_transfer_tips(self): + alice = self.make_participant('alice', claimed_time='now', balance=1) + alice.set_tip_to(self.janet, D('0.51')) + alice.set_tip_to(self.homer, D('0.50')) + payday = Payday.start() + payday.prepare(self.db, payday.ts_start) + payday.transfer_tips(self.db) + payday.update_balances(self.db) + alice = Participant.from_id(alice.id) + assert Participant.from_id(alice.id).balance == D('0.49') + assert Participant.from_id(self.janet.id).balance == D('0.51') + assert Participant.from_id(self.homer.id).balance == 0 + def test_transfer_takes(self): a_team = self.make_participant('a_team', claimed_time='now', number='plural', balance=20) alice = self.make_participant('alice', claimed_time='now') From 445c22863199a8a2f282a30c4a1b41dbfdc5546b Mon Sep 17 00:00:00 2001 From: Changaco Date: Thu, 7 Aug 2014 13:08:42 +0200 Subject: [PATCH 79/97] fix `transfer_tips()` --- gittip/billing/payday.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/gittip/billing/payday.py b/gittip/billing/payday.py index 243b3ceadd..63584d666d 100644 --- a/gittip/billing/payday.py +++ b/gittip/billing/payday.py @@ -252,13 +252,23 @@ def prepare(cursor, ts_start): -- Create a trigger to process tips CREATE OR REPLACE FUNCTION process_tip() RETURNS trigger AS $$ + DECLARE + tipper pay_participants; BEGIN - EXECUTE transfer(NEW.tipper, NEW.tippee, NEW.amount, 'tip'); + tipper := ( + SELECT p.*::pay_participants + FROM pay_participants p + WHERE username = NEW.tipper + ); + IF (NEW.amount <= tipper.new_balance OR tipper.last_bill_result = '') THEN + EXECUTE transfer(NEW.tipper, NEW.tippee, NEW.amount, 'tip'); + RETURN NEW; + END IF; RETURN NULL; END; $$ LANGUAGE plpgsql; - CREATE TRIGGER process_tip AFTER UPDATE OF is_funded ON pay_tips + CREATE TRIGGER process_tip BEFORE UPDATE OF is_funded ON pay_tips FOR EACH ROW WHEN (NEW.is_funded IS true AND OLD.is_funded IS NOT true) EXECUTE PROCEDURE process_tip(); @@ -390,12 +400,7 @@ def transfer_tips(cursor): UPDATE pay_tips t SET is_funded = true - WHERE is_funded IS NOT true - AND amount <= ( - SELECT new_balance - FROM pay_participants p - WHERE p.username = t.tipper - ); + WHERE is_funded IS NOT true; """) From 334f7657520dffad16a0655ea2da5a9d1f7642a5 Mon Sep 17 00:00:00 2001 From: Changaco Date: Thu, 7 Aug 2014 13:24:44 +0200 Subject: [PATCH 80/97] replace foreman with honcho --- payday.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/payday.sh b/payday.sh index 8ca19ac86b..622424849a 100755 --- a/payday.sh +++ b/payday.sh @@ -81,7 +81,7 @@ else fi if [ $1 ]; then - require foreman + require honcho confirm "$RUN payday #$1?" if [ $? -eq 0 ]; then if [ "$2" == "" ]; then @@ -92,7 +92,7 @@ if [ $1 ]; then confirm "$RUN payday #$1 FOR REAL?!?!?!??!?!?" if [ $? -eq 0 ]; then start - heroku config -s | foreman run -e /dev/stdin \ + heroku config -s | honcho run -e /dev/stdin \ ./env/bin/payday >> $LOG 2>&1 fi else From 445ee78bbadc6d2c363c5e5e299d0b85f806f0b6 Mon Sep 17 00:00:00 2001 From: Changaco Date: Thu, 7 Aug 2014 13:26:47 +0200 Subject: [PATCH 81/97] add `./env/bin` to `$PATH` --- payday.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/payday.sh b/payday.sh index 622424849a..b6912d58aa 100755 --- a/payday.sh +++ b/payday.sh @@ -81,19 +81,19 @@ else fi if [ $1 ]; then + PATH="./env/bin:$PATH" require honcho confirm "$RUN payday #$1?" if [ $? -eq 0 ]; then if [ "$2" == "" ]; then start - honcho run -e defaults.env,local.env ./env/bin/payday >> $LOG 2>&1 + honcho run -e defaults.env,local.env payday >>$LOG 2>&1 else if [ "$2" == "for_real_please" ]; then confirm "$RUN payday #$1 FOR REAL?!?!?!??!?!?" if [ $? -eq 0 ]; then start - heroku config -s | honcho run -e /dev/stdin \ - ./env/bin/payday >> $LOG 2>&1 + heroku config -s | honcho run -e /dev/stdin payday >>$LOG 2>&1 fi else echo "Your second arg was $2. Wazzat mean?" From 3d73eb5a928693dae791b7d22517521b3b807d78 Mon Sep 17 00:00:00 2001 From: Changaco Date: Thu, 7 Aug 2014 13:30:52 +0200 Subject: [PATCH 82/97] refactor --- payday.sh | 41 +++++++++++++++++++---------------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/payday.sh b/payday.sh index b6912d58aa..6dcefe231a 100755 --- a/payday.sh +++ b/payday.sh @@ -15,7 +15,7 @@ cd "`dirname $0`" # --help # ====== -if [ $# = 0 ]; then +if [ $# = 0 -o "$1" = "" ]; then echo echo "Usage: $0 [\"for_real_please\"]" echo @@ -80,24 +80,21 @@ else RUN="Run" fi -if [ $1 ]; then - PATH="./env/bin:$PATH" - require honcho - confirm "$RUN payday #$1?" - if [ $? -eq 0 ]; then - if [ "$2" == "" ]; then - start - honcho run -e defaults.env,local.env payday >>$LOG 2>&1 - else - if [ "$2" == "for_real_please" ]; then - confirm "$RUN payday #$1 FOR REAL?!?!?!??!?!?" - if [ $? -eq 0 ]; then - start - heroku config -s | honcho run -e /dev/stdin payday >>$LOG 2>&1 - fi - else - echo "Your second arg was $2. Wazzat mean?" - fi - fi - fi -fi +PATH="./env/bin:$PATH" +require honcho +confirm "$RUN payday #$1?" || exit 0 +case "$2" in + "") + start + honcho run -e defaults.env,local.env payday >>$LOG 2>&1 + ;; + "for_real_please") + confirm "$RUN payday #$1 FOR REAL?!?!?!??!?!?" || exit 0 + start + heroku config -s | honcho run -e /dev/stdin payday >>$LOG 2>&1 + ;; + *) + echo "Your second arg was $2. Wazzat mean?" + exit 1 + ;; +esac From a899e577667e6616dc1b1d73f441d6df1cd37f19 Mon Sep 17 00:00:00 2001 From: Changaco Date: Thu, 7 Aug 2014 13:31:38 +0200 Subject: [PATCH 83/97] run payday in the background and tail the log --- payday.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/payday.sh b/payday.sh index 6dcefe231a..a71b1bc615 100755 --- a/payday.sh +++ b/payday.sh @@ -86,15 +86,18 @@ confirm "$RUN payday #$1?" || exit 0 case "$2" in "") start - honcho run -e defaults.env,local.env payday >>$LOG 2>&1 + honcho run -e defaults.env,local.env payday >>$LOG 2>&1 & ;; "for_real_please") confirm "$RUN payday #$1 FOR REAL?!?!?!??!?!?" || exit 0 start - heroku config -s | honcho run -e /dev/stdin payday >>$LOG 2>&1 + heroku config -s | honcho run -e /dev/stdin payday >>$LOG 2>&1 & ;; *) echo "Your second arg was $2. Wazzat mean?" exit 1 ;; esac + +disown -a +tail -f $LOG From c4948685978dc20ffb891d80ca67781f2b1ad4db Mon Sep 17 00:00:00 2001 From: Changaco Date: Thu, 7 Aug 2014 14:18:50 +0200 Subject: [PATCH 84/97] better error message when exchange fails closes #2441 --- gittip/billing/exchanges.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gittip/billing/exchanges.py b/gittip/billing/exchanges.py index a849bfed4c..ba5a39b921 100644 --- a/gittip/billing/exchanges.py +++ b/gittip/billing/exchanges.py @@ -73,7 +73,7 @@ def skim_credit(amount): def repr_exception(e): if isinstance(e, balanced.exc.HTTPError): - return e.message.message + return '%s %s, %s' % (e.status_code, e.status, e.description) else: return repr(e) From 7c610fff23751aecfff0ac39190b116f288bf36e Mon Sep 17 00:00:00 2001 From: Changaco Date: Thu, 7 Aug 2014 14:28:31 +0200 Subject: [PATCH 85/97] fix test --- tests/py/test_billing_exchanges.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/py/test_billing_exchanges.py b/tests/py/test_billing_exchanges.py index 87648a3536..05aa658a75 100644 --- a/tests/py/test_billing_exchanges.py +++ b/tests/py/test_billing_exchanges.py @@ -144,7 +144,7 @@ def test_create_card_hold_bad_card(self): bob = self.make_participant('bob', balanced_customer_href=customer_href, is_suspicious=False) hold, error = create_card_hold(self.db, bob, D('10.00')) - assert error == '402 Client Error: PAYMENT REQUIRED' + assert error.startswith('402 Payment Required, ') def test_create_card_hold_multiple_cards(self): customer_href = self.make_balanced_customer() From 24db90b4a393c16e0940a53abfb5fe543612accf Mon Sep 17 00:00:00 2001 From: Changaco Date: Thu, 7 Aug 2014 16:45:37 +0200 Subject: [PATCH 86/97] update test to detect another bug --- tests/py/test_billing_payday.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/py/test_billing_payday.py b/tests/py/test_billing_payday.py index a308f006d9..ccffc16744 100644 --- a/tests/py/test_billing_payday.py +++ b/tests/py/test_billing_payday.py @@ -311,7 +311,8 @@ def test_payin_doesnt_make_null_transfers(self): assert not transfers0 def test_transfer_tips(self): - alice = self.make_participant('alice', claimed_time='now', balance=1) + alice = self.make_participant('alice', claimed_time='now', balance=1, + last_bill_result='') alice.set_tip_to(self.janet, D('0.51')) alice.set_tip_to(self.homer, D('0.50')) payday = Payday.start() From 8297378baa4503d4311ab8e32f39ac24d7742d5d Mon Sep 17 00:00:00 2001 From: Changaco Date: Thu, 7 Aug 2014 16:46:00 +0200 Subject: [PATCH 87/97] fix `payin()` to flag which participants we actually have card holds for --- gittip/billing/payday.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/gittip/billing/payday.py b/gittip/billing/payday.py index 63584d666d..ffd2e363e7 100644 --- a/gittip/billing/payday.py +++ b/gittip/billing/payday.py @@ -166,6 +166,7 @@ def prepare(cursor, ts_start): , last_bill_result , is_suspicious , goal + , false AS card_hold_ok FROM participants WHERE is_suspicious IS NOT true AND claimed_time < %(ts_start)s @@ -260,7 +261,7 @@ def prepare(cursor, ts_start): FROM pay_participants p WHERE username = NEW.tipper ); - IF (NEW.amount <= tipper.new_balance OR tipper.last_bill_result = '') THEN + IF (NEW.amount <= tipper.new_balance OR tipper.card_hold_ok) THEN EXECUTE transfer(NEW.tipper, NEW.tippee, NEW.amount, 'tip'); RETURN NEW; END IF; @@ -377,13 +378,14 @@ def create_card_holds(self, cursor): else: holds[p.id] = hold - # Update the values of last_bill_result in our temporary table + # Update the values of card_hold_ok in our temporary table + if not holds: + return {} cursor.run(""" UPDATE pay_participants p - SET last_bill_result = p2.last_bill_result - FROM participants p2 - WHERE p.id = p2.id - """) + SET card_hold_ok = true + WHERE p.id IN %s + """, (tuple(holds.keys()),)) return holds @@ -396,7 +398,7 @@ def transfer_tips(cursor): SET is_funded = true FROM pay_participants p WHERE p.username = t.tipper - AND p.last_bill_result = ''; + AND p.card_hold_ok; UPDATE pay_tips t SET is_funded = true From 1e0a0a7cf513d6f3739ac35ba23e357ff490a8a1 Mon Sep 17 00:00:00 2001 From: Changaco Date: Thu, 7 Aug 2014 17:18:24 +0200 Subject: [PATCH 88/97] fix minor bug in `update_stats()` --- gittip/billing/payday.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gittip/billing/payday.py b/gittip/billing/payday.py index ffd2e363e7..de7349220a 100644 --- a/gittip/billing/payday.py +++ b/gittip/billing/payday.py @@ -602,7 +602,7 @@ def update_stats(self): SELECT COALESCE(sum(amount + fee), 0) FROM our_charges ) - , charge_fees_volume = (SELECT sum(fee) FROM our_charges) + , charge_fees_volume = (SELECT COALESCE(sum(fee), 0) FROM our_charges) WHERE ts_end='1970-01-01T00:00:00+00'::timestamptz """, {'ts_start': self.ts_start}) From e8ce300cd2dd7418837ea9bec288e3d71284fc6b Mon Sep 17 00:00:00 2001 From: Chad Whitacre Date: Thu, 7 Aug 2014 13:18:51 -0400 Subject: [PATCH 89/97] Let's put the populate-balanced.py script in git --- branch.py | 107 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 branch.py diff --git a/branch.py b/branch.py new file mode 100644 index 0000000000..17cbe729fb --- /dev/null +++ b/branch.py @@ -0,0 +1,107 @@ +"""Populate a Balanced test marketplace per a db. +""" +import balanced +from gittip import wireup + + +print "Wiring up ..." + +env = wireup.env() +wireup.billing(env) +db = wireup.db(env) + + +print "Populating Balanced ..." + +no_card_no_bank = balanced.Customer(email='TEST-no-card-no-bank@gittip.com').save() +no_card_good_bank = balanced.Customer(email='TEST-no-card-good-bank@gittip.com').save() +no_card_bad_bank = balanced.Customer(email='TEST-no-card-bad-bank@gittip.com').save() +good_card_no_bank = balanced.Customer(email='TEST-good-card-no-bank@gittip.com').save() +good_card_good_bank = balanced.Customer(email='TEST-good-card-good-bank@gittip.com').save() +good_card_bad_bank = balanced.Customer(email='TEST-good-card-bad-bank@gittip.com').save() +bad_card_no_bank = balanced.Customer(email='TEST-bad-card-no-bank@gittip.com').save() +bad_card_good_bank = balanced.Customer(email='TEST-bad-card-good-bank@gittip.com').save() +bad_card_bad_bank = balanced.Customer(email='TEST-bad-card-bad-bank@gittip.com').save() + +no_card_no_bank +no_card_good_bank +no_card_bad_bank +good_card_no_bank +good_card_good_bank +good_card_bad_bank +bad_card_no_bank +bad_card_good_bank +bad_card_bad_bank + +# https://docs.balancedpayments.com/1.1/overview/resources/#test-credit-card-numbers +good_card = lambda: balanced.Card( number="4111111111111111" + , expiration_month="12" + , expiration_year="2015").save() +bad_card = lambda: balanced.Card( number="4444444444444448" + , expiration_month="12" + , expiration_year="2015").save() + +# https://docs.balancedpayments.com/1.1/overview/resources/#test-bank-account-numbers +good_bank = lambda: balanced.BankAccount( account_number="9900000002" + , routing_number="021000021" + , name="Foo").save() +bad_bank = lambda: balanced.BankAccount( account_number="9900000004" + , routing_number="021000021" + , name="Foo").save() + +good_card().associate_to_customer(good_card_no_bank) +good_card().associate_to_customer(good_card_good_bank) +good_card().associate_to_customer(good_card_bad_bank) + +bad_card().associate_to_customer(bad_card_no_bank) +bad_card().associate_to_customer(bad_card_good_bank) +bad_card().associate_to_customer(bad_card_bad_bank) + +good_bank().associate_to_customer(no_card_good_bank) +good_bank().associate_to_customer(good_card_good_bank) +good_bank().associate_to_customer(bad_card_good_bank) + +bad_bank().associate_to_customer(no_card_bad_bank) +bad_bank().associate_to_customer(good_card_bad_bank) +bad_bank().associate_to_customer(bad_card_bad_bank) + + +print "Updating DB ..." + +participants = db.all("SELECT p.*::participants from participants p " + "WHERE balanced_customer_href IS NOT NULL") + +for participant in participants: + + customer = None + card = participant.last_bill_result + bank = participant.last_ach_result + + if card is None: + if bank is None: + customer = no_card_no_bank + elif bank == '': + customer = no_card_good_bank + elif bank > '': + customer = no_card_bad_bank + elif card == '': + if bank is None: + customer = good_card_no_bank + elif bank == '': + customer = good_card_good_bank + elif bank > '': + customer = good_card_bad_bank + elif card > '': + if bank is None: + customer = bad_card_no_bank + elif bank == '': + customer = bad_card_good_bank + elif bank > '': + customer = bad_card_bad_bank + + print customer.href + + db.run( "UPDATE participants SET balanced_customer_href=%s WHERE id=%s" + , (customer.href, participant.id)) + +print len(participants) From 16720839dd3147f349f21e547ea43a5f578e9168 Mon Sep 17 00:00:00 2001 From: Changaco Date: Fri, 8 Aug 2014 10:31:39 +0200 Subject: [PATCH 90/97] fix `UPDATE` query in `prepare()` --- gittip/billing/payday.py | 1 + 1 file changed, 1 insertion(+) diff --git a/gittip/billing/payday.py b/gittip/billing/payday.py index de7349220a..c9839bea3e 100644 --- a/gittip/billing/payday.py +++ b/gittip/billing/payday.py @@ -314,6 +314,7 @@ def prepare(cursor, ts_start): last_bill_result IS NULL ) ) + WHERE ts_end='1970-01-01T00:00:00+00'::timestamptz; """, dict(ts_start=ts_start)) log('Prepared the DB.') From b7b60461f7b31299c837a3b0f964ef89bd89487b Mon Sep 17 00:00:00 2001 From: Changaco Date: Fri, 8 Aug 2014 11:51:10 +0200 Subject: [PATCH 91/97] failing tests for `NoResultFound()` --- gittip/testing/balanced.py | 3 ++- tests/py/fixtures/TestCredits.yml | 16 ++++++++++++++++ tests/py/test_billing_exchanges.py | 9 +++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/gittip/testing/balanced.py b/gittip/testing/balanced.py index b9a431ae38..c0ef76474a 100644 --- a/gittip/testing/balanced.py +++ b/gittip/testing/balanced.py @@ -9,7 +9,8 @@ class BalancedHarness(Harness): def setUp(self): - self.david = self.make_participant('david', claimed_time='now', + self.david = self.make_participant('david', is_suspicious=False, + claimed_time='now', balanced_customer_href=self.david_href) self.janet = self.make_participant('janet', is_suspicious=False, claimed_time='now', diff --git a/tests/py/fixtures/TestCredits.yml b/tests/py/fixtures/TestCredits.yml index f2a916c151..ec6bb8544e 100644 --- a/tests/py/fixtures/TestCredits.yml +++ b/tests/py/fixtures/TestCredits.yml @@ -48,4 +48,20 @@ interactions: content-length: ['1018'] content-type: [application/json] status: {code: 201, message: CREATED} +- request: + body: null + headers: {} + method: GET + uri: https://api.balancedpayments.com:443/customers/CU3KoKRb123DPYH3y6rw4OmF/bank_accounts?limit=10&offset=0 + response: + body: {string: !!python/unicode "{\n \"bank_accounts\": [],\n \"meta\": {\n + \ \"last\": \"/customers/CU3KoKRb123DPYH3y6rw4OmF/bank_accounts?limit=10&offset=0\",\n + \ \"next\": null,\n \"href\": \"/customers/CU3KoKRb123DPYH3y6rw4OmF/bank_accounts?limit=10&offset=0\",\n + \ \"limit\": 10,\n \"offset\": 0,\n \"previous\": null,\n \"total\": + 0,\n \"first\": \"/customers/CU3KoKRb123DPYH3y6rw4OmF/bank_accounts?limit=10&offset=0\"\n + \ },\n \"links\": {}\n}"} + headers: + content-length: ['396'] + content-type: [application/json] + status: {code: 200, message: OK} version: 1 diff --git a/tests/py/test_billing_exchanges.py b/tests/py/test_billing_exchanges.py index 05aa658a75..c54722ea04 100644 --- a/tests/py/test_billing_exchanges.py +++ b/tests/py/test_billing_exchanges.py @@ -56,6 +56,13 @@ def test_ach_credit_failure(self, Customer): assert bob.last_ach_result == bob2.last_ach_result == error == "Foobar()" assert bob.balance == bob2.balance == 20 + def test_ach_credit_no_bank_account(self): + self.make_exchange('bill', 20, 0, self.david) + error = ach_credit(self.db, self.david, D('1.00')) + david = Participant.from_username('david') + assert error == 'NoResultFound()' + assert self.david.last_ach_result == david.last_ach_result == None + class TestCardHolds(BalancedHarness): @@ -172,7 +179,9 @@ def test_create_card_hold_no_card(self): bob = self.make_participant('bob', balanced_customer_href=customer_href, is_suspicious=False) hold, error = create_card_hold(self.db, bob, D('10.00')) + bob2 = Participant.from_id(bob.id) assert error == 'NoResultFound()' + assert bob.last_bill_result == bob2.last_bill_result == None class TestFees(Harness): From e952383ea531db98fdb8a9ff896d7222f29d1495 Mon Sep 17 00:00:00 2001 From: Changaco Date: Fri, 8 Aug 2014 11:55:21 +0200 Subject: [PATCH 92/97] get rid of `NoResultFound()` --- branch.sql | 7 +++++++ gittip/billing/exchanges.py | 5 ++--- tests/py/test_billing.py | 4 ++-- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/branch.sql b/branch.sql index a5d7df7f67..fd366b6db0 100644 --- a/branch.sql +++ b/branch.sql @@ -6,4 +6,11 @@ BEGIN; CREATE TYPE exchange_status AS ENUM ('pre', 'pending', 'failed', 'succeeded'); ALTER TABLE exchanges ADD COLUMN status exchange_status; + UPDATE participants + SET last_ach_result = NULL + WHERE last_ach_result = 'NoResultFound()'; + UPDATE participants + SET last_bill_result = NULL + WHERE last_bill_result = 'NoResultFound()'; + END; diff --git a/gittip/billing/exchanges.py b/gittip/billing/exchanges.py index ba5a39b921..a5750d93ef 100644 --- a/gittip/billing/exchanges.py +++ b/gittip/billing/exchanges.py @@ -194,8 +194,7 @@ def create_card_hold(db, participant, amount): except Exception as e: error = repr_exception(e) log(msg + "failed: %s" % error) - db.run("UPDATE participants SET last_bill_result=%s WHERE id=%s", - (error, participant.id)) + propagate_exchange(db, participant, 'bill', error, 0) return hold, error @@ -322,7 +321,7 @@ def propagate_exchange(cursor, participant, kind, error, amount): """Propagates an exchange to the participant's balance. """ column = 'last_%s_result' % kind - error = error or '' + error = None if error == 'NoResultFound()' else (error or '') new_balance = cursor.one(""" UPDATE participants SET {0}=%s diff --git a/tests/py/test_billing.py b/tests/py/test_billing.py index 5df59f72ae..f6703b2f19 100644 --- a/tests/py/test_billing.py +++ b/tests/py/test_billing.py @@ -38,9 +38,9 @@ def test_credit_card_page_loads_when_there_is_a_card(self): actual = self.client.GET('/credit-card.html', auth_as='janet').body.decode('utf8') assert expected in actual - def test_credit_card_page_loads_when_there_is_an_account_but_no_card(self): + def test_credit_card_page_shows_card_failing(self): self.db.run( "UPDATE participants " - "SET last_bill_result='NoResultFound()'" + "SET last_bill_result='Some error'" "WHERE username='janet'" ) From a6d6cff6af10e10eb0e0649e2731cb2c9c8e7bce Mon Sep 17 00:00:00 2001 From: Chad Whitacre Date: Fri, 8 Aug 2014 12:48:50 -0400 Subject: [PATCH 93/97] Update branch.py to work with a cache --- balanced.cache | 9 ++++ branch.py | 126 ++++++++++++++++++++++++++++--------------------- 2 files changed, 82 insertions(+), 53 deletions(-) create mode 100644 balanced.cache diff --git a/balanced.cache b/balanced.cache new file mode 100644 index 0000000000..fc433c4927 --- /dev/null +++ b/balanced.cache @@ -0,0 +1,9 @@ +/customers/CU2AeM9eNm4xOlzMf2giQLSM +/customers/CU2AB9rAnNMIk2WVUYzTM5bO +/customers/CU2AXHbONP7DZ2icdaC2SKod +/customers/CU2C8JIiShKQwRsfx1lApiCN +/customers/CU2CykeO7fm4GdldRFL9gvEF +/customers/CU2CVUDM4bDUxbDiFQdCFrJS +/customers/CU2DhhxA0V2thT9S8ycuMff7 +/customers/CU2DJQgKRESgGP12GHikqsla +/customers/CU2E60G2l5ekRRlIQK6CMHch diff --git a/branch.py b/branch.py index 17cbe729fb..4b45fcf077 100644 --- a/branch.py +++ b/branch.py @@ -2,6 +2,7 @@ """ import balanced from gittip import wireup +from gittip.billing.exchanges import customer_from_href print "Wiring up ..." @@ -11,59 +12,78 @@ db = wireup.db(env) -print "Populating Balanced ..." - -no_card_no_bank = balanced.Customer(email='TEST-no-card-no-bank@gittip.com').save() -no_card_good_bank = balanced.Customer(email='TEST-no-card-good-bank@gittip.com').save() -no_card_bad_bank = balanced.Customer(email='TEST-no-card-bad-bank@gittip.com').save() -good_card_no_bank = balanced.Customer(email='TEST-good-card-no-bank@gittip.com').save() -good_card_good_bank = balanced.Customer(email='TEST-good-card-good-bank@gittip.com').save() -good_card_bad_bank = balanced.Customer(email='TEST-good-card-bad-bank@gittip.com').save() -bad_card_no_bank = balanced.Customer(email='TEST-bad-card-no-bank@gittip.com').save() -bad_card_good_bank = balanced.Customer(email='TEST-bad-card-good-bank@gittip.com').save() -bad_card_bad_bank = balanced.Customer(email='TEST-bad-card-bad-bank@gittip.com').save() - -no_card_no_bank -no_card_good_bank -no_card_bad_bank -good_card_no_bank -good_card_good_bank -good_card_bad_bank -bad_card_no_bank -bad_card_good_bank -bad_card_bad_bank - -# https://docs.balancedpayments.com/1.1/overview/resources/#test-credit-card-numbers -good_card = lambda: balanced.Card( number="4111111111111111" - , expiration_month="12" - , expiration_year="2015").save() -bad_card = lambda: balanced.Card( number="4444444444444448" - , expiration_month="12" - , expiration_year="2015").save() - -# https://docs.balancedpayments.com/1.1/overview/resources/#test-bank-account-numbers -good_bank = lambda: balanced.BankAccount( account_number="9900000002" - , routing_number="021000021" - , name="Foo").save() -bad_bank = lambda: balanced.BankAccount( account_number="9900000004" - , routing_number="021000021" - , name="Foo").save() - -good_card().associate_to_customer(good_card_no_bank) -good_card().associate_to_customer(good_card_good_bank) -good_card().associate_to_customer(good_card_bad_bank) - -bad_card().associate_to_customer(bad_card_no_bank) -bad_card().associate_to_customer(bad_card_good_bank) -bad_card().associate_to_customer(bad_card_bad_bank) - -good_bank().associate_to_customer(no_card_good_bank) -good_bank().associate_to_customer(good_card_good_bank) -good_bank().associate_to_customer(bad_card_good_bank) - -bad_bank().associate_to_customer(no_card_bad_bank) -bad_bank().associate_to_customer(good_card_bad_bank) -bad_bank().associate_to_customer(bad_card_bad_bank) +try: + cache = open('balanced.cache').read().splitlines() + import pdb; pdb.set_trace() + print "Populating Balanced from cache ..." + + no_card_no_bank = customer_from_href(cache[0]) + no_card_good_bank = customer_from_href(cache[1]) + no_card_bad_bank = customer_from_href(cache[2]) + good_card_no_bank = customer_from_href(cache[3]) + good_card_good_bank = customer_from_href(cache[4]) + good_card_bad_bank = customer_from_href(cache[5]) + bad_card_no_bank = customer_from_href(cache[6]) + bad_card_good_bank = customer_from_href(cache[7]) + bad_card_bad_bank = customer_from_href(cache[8]) + +except IOError: + print "Populating Balanced ..." + + no_card_no_bank = balanced.Customer(email='TEST-no-card-no-bank@gittip.com').save() + no_card_good_bank = balanced.Customer(email='TEST-no-card-good-bank@gittip.com').save() + no_card_bad_bank = balanced.Customer(email='TEST-no-card-bad-bank@gittip.com').save() + good_card_no_bank = balanced.Customer(email='TEST-good-card-no-bank@gittip.com').save() + good_card_good_bank = balanced.Customer(email='TEST-good-card-good-bank@gittip.com').save() + good_card_bad_bank = balanced.Customer(email='TEST-good-card-bad-bank@gittip.com').save() + bad_card_no_bank = balanced.Customer(email='TEST-bad-card-no-bank@gittip.com').save() + bad_card_good_bank = balanced.Customer(email='TEST-bad-card-good-bank@gittip.com').save() + bad_card_bad_bank = balanced.Customer(email='TEST-bad-card-bad-bank@gittip.com').save() + + + # https://docs.balancedpayments.com/1.1/overview/resources/#test-credit-card-numbers + good_card = lambda: balanced.Card( number="4111111111111111" + , expiration_month="12" + , expiration_year="2015").save() + bad_card = lambda: balanced.Card( number="4444444444444448" + , expiration_month="12" + , expiration_year="2015").save() + + # https://docs.balancedpayments.com/1.1/overview/resources/#test-bank-account-numbers + good_bank = lambda: balanced.BankAccount( account_number="9900000002" + , routing_number="021000021" + , name="Foo").save() + bad_bank = lambda: balanced.BankAccount( account_number="9900000004" + , routing_number="021000021" + , name="Foo").save() + + good_card().associate_to_customer(good_card_no_bank) + good_card().associate_to_customer(good_card_good_bank) + good_card().associate_to_customer(good_card_bad_bank) + + bad_card().associate_to_customer(bad_card_no_bank) + bad_card().associate_to_customer(bad_card_good_bank) + bad_card().associate_to_customer(bad_card_bad_bank) + + good_bank().associate_to_customer(no_card_good_bank) + good_bank().associate_to_customer(good_card_good_bank) + good_bank().associate_to_customer(bad_card_good_bank) + + bad_bank().associate_to_customer(no_card_bad_bank) + bad_bank().associate_to_customer(good_card_bad_bank) + bad_bank().associate_to_customer(bad_card_bad_bank) + + customers = [ no_card_no_bank + , no_card_good_bank + , no_card_bad_bank + , good_card_no_bank + , good_card_good_bank + , good_card_bad_bank + , bad_card_no_bank + , bad_card_good_bank + , bad_card_bad_bank + ] + open('balanced.cache', 'w+').write('\n'.join([customer.href for customer in customers])) print "Updating DB ..." From 9a28447d1ac6dcfcca77cfb95c8e5d0605578ca9 Mon Sep 17 00:00:00 2001 From: Changaco Date: Sat, 9 Aug 2014 10:53:29 +0200 Subject: [PATCH 94/97] =?UTF-8?q?rename=20SQL=20tables=20`pay=5F*`=20?= =?UTF-8?q?=E2=86=92=20`payday=5F*`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gittip/billing/payday.py | 84 ++++++++++++++++----------------- tests/py/test_billing_payday.py | 4 +- 2 files changed, 44 insertions(+), 44 deletions(-) diff --git a/gittip/billing/payday.py b/gittip/billing/payday.py index c9839bea3e..0b94cde8ad 100644 --- a/gittip/billing/payday.py +++ b/gittip/billing/payday.py @@ -155,8 +155,8 @@ def prepare(cursor, ts_start): -- Create the necessary temporary tables and indexes - DROP TABLE IF EXISTS pay_participants CASCADE; - CREATE TEMPORARY TABLE pay_participants AS + DROP TABLE IF EXISTS payday_participants CASCADE; + CREATE TEMPORARY TABLE payday_participants AS SELECT id , username , claimed_time @@ -172,49 +172,49 @@ def prepare(cursor, ts_start): AND claimed_time < %(ts_start)s ORDER BY claimed_time; - CREATE UNIQUE INDEX ON pay_participants (id); - CREATE UNIQUE INDEX ON pay_participants (username); + CREATE UNIQUE INDEX ON payday_participants (id); + CREATE UNIQUE INDEX ON payday_participants (username); - DROP TABLE IF EXISTS pay_transfers CASCADE; - CREATE TEMPORARY TABLE pay_transfers AS + DROP TABLE IF EXISTS payday_transfers CASCADE; + CREATE TEMPORARY TABLE payday_transfers AS SELECT * FROM transfers t WHERE t.timestamp > %(ts_start)s; - DROP TABLE IF EXISTS pay_tips CASCADE; - CREATE TEMPORARY TABLE pay_tips AS + DROP TABLE IF EXISTS payday_tips CASCADE; + CREATE TEMPORARY TABLE payday_tips AS SELECT tipper, tippee, amount FROM ( SELECT DISTINCT ON (tipper, tippee) * FROM tips WHERE mtime < %(ts_start)s ORDER BY tipper, tippee, mtime DESC ) t - JOIN pay_participants p ON p.username = t.tipper - JOIN pay_participants p2 ON p2.username = t.tippee + JOIN payday_participants p ON p.username = t.tipper + JOIN payday_participants p2 ON p2.username = t.tippee WHERE t.amount > 0 AND (p2.goal IS NULL or p2.goal >= 0) AND ( SELECT id - FROM pay_transfers t2 + FROM payday_transfers t2 WHERE t.tipper = t2.tipper AND t.tippee = t2.tippee AND context = 'tip' ) IS NULL ORDER BY p.claimed_time ASC, t.ctime ASC; - CREATE INDEX ON pay_tips (tipper); - CREATE INDEX ON pay_tips (tippee); - ALTER TABLE pay_tips ADD COLUMN is_funded boolean; + CREATE INDEX ON payday_tips (tipper); + CREATE INDEX ON payday_tips (tippee); + ALTER TABLE payday_tips ADD COLUMN is_funded boolean; - ALTER TABLE pay_participants ADD COLUMN giving_today numeric(35,2); - UPDATE pay_participants + ALTER TABLE payday_participants ADD COLUMN giving_today numeric(35,2); + UPDATE payday_participants SET giving_today = COALESCE(( SELECT sum(amount) - FROM pay_tips + FROM payday_tips WHERE tipper = username ), 0); - DROP TABLE IF EXISTS pay_takes; - CREATE TEMPORARY TABLE pay_takes + DROP TABLE IF EXISTS payday_takes; + CREATE TEMPORARY TABLE payday_takes ( team text , member text , amount numeric(35,2) @@ -227,21 +227,21 @@ def prepare(cursor, ts_start): RETURNS void AS $$ BEGIN IF ($3 = 0) THEN RETURN; END IF; - UPDATE pay_participants + UPDATE payday_participants SET new_balance = (new_balance - $3) WHERE username = $1; - UPDATE pay_participants + UPDATE payday_participants SET new_balance = (new_balance + $3) WHERE username = $2; INSERT INTO transfers (tipper, tippee, amount, context) VALUES ( ( SELECT p.username FROM participants p - JOIN pay_participants p2 ON p.id = p2.id + JOIN payday_participants p2 ON p.id = p2.id WHERE p2.username = $1 ) , ( SELECT p.username FROM participants p - JOIN pay_participants p2 ON p.id = p2.id + JOIN payday_participants p2 ON p.id = p2.id WHERE p2.username = $2 ) , $3 , $4 @@ -254,11 +254,11 @@ def prepare(cursor, ts_start): CREATE OR REPLACE FUNCTION process_tip() RETURNS trigger AS $$ DECLARE - tipper pay_participants; + tipper payday_participants; BEGIN tipper := ( - SELECT p.*::pay_participants - FROM pay_participants p + SELECT p.*::payday_participants + FROM payday_participants p WHERE username = NEW.tipper ); IF (NEW.amount <= tipper.new_balance OR tipper.card_hold_ok) THEN @@ -269,7 +269,7 @@ def prepare(cursor, ts_start): END; $$ LANGUAGE plpgsql; - CREATE TRIGGER process_tip BEFORE UPDATE OF is_funded ON pay_tips + CREATE TRIGGER process_tip BEFORE UPDATE OF is_funded ON payday_tips FOR EACH ROW WHEN (NEW.is_funded IS true AND OLD.is_funded IS NOT true) EXECUTE PROCEDURE process_tip(); @@ -284,7 +284,7 @@ def prepare(cursor, ts_start): BEGIN team_balance := ( SELECT new_balance - FROM pay_participants + FROM payday_participants WHERE username = NEW.team ); IF (team_balance <= 0) THEN RETURN NULL; END IF; @@ -297,17 +297,17 @@ def prepare(cursor, ts_start): END; $$ LANGUAGE plpgsql; - CREATE TRIGGER process_take AFTER INSERT ON pay_takes + CREATE TRIGGER process_take AFTER INSERT ON payday_takes FOR EACH ROW EXECUTE PROCEDURE process_take(); -- Save the stats we already have UPDATE paydays - SET nparticipants = (SELECT count(*) FROM pay_participants) + SET nparticipants = (SELECT count(*) FROM payday_participants) , ncc_missing = ( SELECT count(*) - FROM pay_participants + FROM payday_participants WHERE old_balance < giving_today AND ( balanced_customer_href IS NULL OR @@ -348,7 +348,7 @@ def create_card_holds(self, cursor): # Get the list of participants to create card holds for participants = cursor.all(""" SELECT * - FROM pay_participants + FROM payday_participants WHERE old_balance < giving_today AND balanced_customer_href IS NOT NULL AND last_bill_result IS NOT NULL @@ -383,7 +383,7 @@ def create_card_holds(self, cursor): if not holds: return {} cursor.run(""" - UPDATE pay_participants p + UPDATE payday_participants p SET card_hold_ok = true WHERE p.id IN %s """, (tuple(holds.keys()),)) @@ -395,13 +395,13 @@ def create_card_holds(self, cursor): def transfer_tips(cursor): cursor.run(""" - UPDATE pay_tips t + UPDATE payday_tips t SET is_funded = true - FROM pay_participants p + FROM payday_participants p WHERE p.username = t.tipper AND p.card_hold_ok; - UPDATE pay_tips t + UPDATE payday_tips t SET is_funded = true WHERE is_funded IS NOT true; @@ -412,7 +412,7 @@ def transfer_tips(cursor): def transfer_takes(cursor, ts_start): cursor.run(""" - INSERT INTO pay_takes + INSERT INTO payday_takes SELECT team, member, amount FROM ( SELECT DISTINCT ON (team, member) team, member, amount, ctime @@ -421,10 +421,10 @@ def transfer_takes(cursor, ts_start): ORDER BY team, member, mtime DESC ) t WHERE t.amount > 0 - AND t.team IN (SELECT username FROM pay_participants) - AND t.member IN (SELECT username FROM pay_participants) + AND t.team IN (SELECT username FROM payday_participants) + AND t.member IN (SELECT username FROM payday_participants) AND ( SELECT id - FROM pay_transfers t2 + FROM payday_transfers t2 WHERE t.team = t2.tipper AND t.member = t2.tippee AND context = 'take' @@ -437,7 +437,7 @@ def transfer_takes(cursor, ts_start): def settle_card_holds(self, cursor, holds): participants = cursor.all(""" SELECT * - FROM pay_participants + FROM payday_participants WHERE new_balance < 0 """) @@ -462,7 +462,7 @@ def update_balances(cursor): UPDATE participants p SET balance = (balance + p2.new_balance - p2.old_balance) - FROM pay_participants p2 + FROM payday_participants p2 WHERE p.id = p2.id AND p2.new_balance <> p2.old_balance RETURNING p.id diff --git a/tests/py/test_billing_payday.py b/tests/py/test_billing_payday.py index ccffc16744..0d74ebed1e 100644 --- a/tests/py/test_billing_payday.py +++ b/tests/py/test_billing_payday.py @@ -124,7 +124,7 @@ def test_start_prepare(self, log): payday = Payday.start() ts_start = payday.ts_start - get_participants = lambda: self.db.all("SELECT * FROM pay_participants") + get_participants = lambda: self.db.all("SELECT * FROM payday_participants") payday.prepare(self.db, ts_start) @@ -270,7 +270,7 @@ def test_payin_cant_make_balances_more_negative(self): with self.db.get_cursor() as cursor: payday.prepare(cursor, payday.ts_start) cursor.run(""" - UPDATE pay_participants + UPDATE payday_participants SET new_balance = -50 WHERE username IN ('janet', 'homer') """) From f27a9525ba0c922ebf7bd66987e6bf4bfe41fc75 Mon Sep 17 00:00:00 2001 From: Changaco Date: Sat, 9 Aug 2014 11:16:28 +0200 Subject: [PATCH 95/97] replace `DROP TABLE IF EXISTS` by `ON COMMIT DROP` --- gittip/billing/payday.py | 12 ++++-------- tests/py/test_billing_payday.py | 27 +++++++++++++++------------ 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/gittip/billing/payday.py b/gittip/billing/payday.py index 0b94cde8ad..1d8450dd16 100644 --- a/gittip/billing/payday.py +++ b/gittip/billing/payday.py @@ -155,8 +155,7 @@ def prepare(cursor, ts_start): -- Create the necessary temporary tables and indexes - DROP TABLE IF EXISTS payday_participants CASCADE; - CREATE TEMPORARY TABLE payday_participants AS + CREATE TEMPORARY TABLE payday_participants ON COMMIT DROP AS SELECT id , username , claimed_time @@ -175,14 +174,12 @@ def prepare(cursor, ts_start): CREATE UNIQUE INDEX ON payday_participants (id); CREATE UNIQUE INDEX ON payday_participants (username); - DROP TABLE IF EXISTS payday_transfers CASCADE; - CREATE TEMPORARY TABLE payday_transfers AS + CREATE TEMPORARY TABLE payday_transfers ON COMMIT DROP AS SELECT * FROM transfers t WHERE t.timestamp > %(ts_start)s; - DROP TABLE IF EXISTS payday_tips CASCADE; - CREATE TEMPORARY TABLE payday_tips AS + CREATE TEMPORARY TABLE payday_tips ON COMMIT DROP AS SELECT tipper, tippee, amount FROM ( SELECT DISTINCT ON (tipper, tippee) * FROM tips @@ -213,12 +210,11 @@ def prepare(cursor, ts_start): WHERE tipper = username ), 0); - DROP TABLE IF EXISTS payday_takes; CREATE TEMPORARY TABLE payday_takes ( team text , member text , amount numeric(35,2) - ); + ) ON COMMIT DROP; -- Prepare a statement that makes and records a transfer diff --git a/tests/py/test_billing_payday.py b/tests/py/test_billing_payday.py index 0d74ebed1e..8e945bf822 100644 --- a/tests/py/test_billing_payday.py +++ b/tests/py/test_billing_payday.py @@ -124,11 +124,11 @@ def test_start_prepare(self, log): payday = Payday.start() ts_start = payday.ts_start - get_participants = lambda: self.db.all("SELECT * FROM payday_participants") + get_participants = lambda c: c.all("SELECT * FROM payday_participants") - payday.prepare(self.db, ts_start) - - participants = get_participants() + with self.db.get_cursor() as cursor: + payday.prepare(cursor, ts_start) + participants = get_participants(cursor) expected_logging_call_args = [ ('Starting a new payday.'), @@ -144,8 +144,9 @@ def test_start_prepare(self, log): # run a second time, we should see it pick up the existing payday payday = Payday.start() second_ts_start = payday.ts_start - payday.prepare(self.db, second_ts_start) - second_participants = get_participants() + with self.db.get_cursor() as cursor: + payday.prepare(cursor, second_ts_start) + second_participants = get_participants(cursor) assert ts_start == second_ts_start participants = list(participants) @@ -316,9 +317,10 @@ def test_transfer_tips(self): alice.set_tip_to(self.janet, D('0.51')) alice.set_tip_to(self.homer, D('0.50')) payday = Payday.start() - payday.prepare(self.db, payday.ts_start) - payday.transfer_tips(self.db) - payday.update_balances(self.db) + with self.db.get_cursor() as cursor: + payday.prepare(cursor, payday.ts_start) + payday.transfer_tips(cursor) + payday.update_balances(cursor) alice = Participant.from_id(alice.id) assert Participant.from_id(alice.id).balance == D('0.49') assert Participant.from_id(self.janet.id).balance == D('0.51') @@ -339,9 +341,10 @@ def test_transfer_takes(self): # Run the transfer multiple times to make sure we ignore takes that # have already been processed for i in range(3): - payday.prepare(self.db, payday.ts_start) - payday.transfer_takes(self.db, payday.ts_start) - payday.update_balances(self.db) + with self.db.get_cursor() as cursor: + payday.prepare(cursor, payday.ts_start) + payday.transfer_takes(cursor, payday.ts_start) + payday.update_balances(cursor) participants = self.db.all("SELECT username, balance FROM participants") From 4b61dbd04d9893290fe8cfaee3e25c8dd0652db5 Mon Sep 17 00:00:00 2001 From: Changaco Date: Fri, 1 Aug 2014 14:50:08 +0200 Subject: [PATCH 96/97] zero out known anomalous tip --- branch.sql | 3 +++ 1 file changed, 3 insertions(+) diff --git a/branch.sql b/branch.sql index fd366b6db0..f54edeeb3e 100644 --- a/branch.sql +++ b/branch.sql @@ -13,4 +13,7 @@ BEGIN; SET last_bill_result = NULL WHERE last_bill_result = 'NoResultFound()'; + INSERT INTO tips (ctime, tipper, tippee, amount) + SELECT ctime, tipper, tippee, 0 FROM tips WHERE id = 46266; + END; From 26ea08584bdb336903117318273168f11b4d009e Mon Sep 17 00:00:00 2001 From: Changaco Date: Fri, 1 Aug 2014 14:51:26 +0200 Subject: [PATCH 97/97] optimize DB check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Total runtime: 52570.561 ms → 118.837 ms --- gittip/models/__init__.py | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/gittip/models/__init__.py b/gittip/models/__init__.py index 922f04b650..8c1fc72a93 100644 --- a/gittip/models/__init__.py +++ b/gittip/models/__init__.py @@ -126,25 +126,15 @@ def _check_orphans_no_tips(cursor): This should not happen because when we remove the last elsewhere account in take_over we also zero out all tips. """ - tips_with_orphans = cursor.all(""" - WITH orphans AS ( - SELECT username FROM participants - WHERE NOT EXISTS (SELECT 1 FROM elsewhere WHERE participant=username) - ), valid_tips AS ( - SELECT * FROM ( - SELECT DISTINCT ON (tipper, tippee) * - FROM tips - ORDER BY tipper, tippee, mtime DESC - ) AS foo - WHERE amount > 0 - ) - SELECT id FROM valid_tips - WHERE tipper IN (SELECT * FROM orphans) - OR tippee IN (SELECT * FROM orphans) + orphans_with_tips = cursor.all(""" + WITH valid_tips AS (SELECT * FROM current_tips WHERE amount > 0) + SELECT username + FROM (SELECT tipper AS username FROM valid_tips + UNION + SELECT tippee AS username FROM valid_tips) foo + WHERE NOT EXISTS (SELECT 1 FROM elsewhere WHERE participant=username) """) - known = set([25206, 46266]) # '4c074000c7bc', 'naderman', '3.00' - real = set(tips_with_orphans) - known - assert len(real) == 0, real + assert len(orphans_with_tips) == 0, orphans_with_tips def _check_paydays_volumes(cursor):