We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
fix regression in receiving amounts
Stripe is giving to Bundler now, but Bundler's "receiving" doesn't reflect this, due to a regression with #2352. [IRC](https://botbot.me/freenode/gittip/2014-06-12/?msg=16141806&page=1)

cc: @indirect @gdb


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `gittip/billing/__init__.py`
Content:
```
1 """This module encapsulates billing logic and db access.
2 
3 There are three pieces of information for each participant related to billing:
4 
5     balanced_customer_href
6         * NULL - This participant has never been billed.
7         * 'deadbeef' - This participant has had a Balanced account created for
8           them, either by adding a credit card or a bank account.
9     last_bill_result
10         * NULL - This participant has not had their credit card charged yet.
11         * '' - This participant has a working card.
12         * <message> - An error message.
13     last_ach_result
14         * NULL - This participant has not wired up a bank account yet.
15         * '' - This participant has a working bank account.
16         * <message> - An error message.
17 
18 """
19 from __future__ import unicode_literals
20 
21 import balanced
22 from aspen.utils import typecheck
23 
24 
25 def get_balanced_account(db, username, balanced_customer_href):
26     """Find or create a balanced.Account.
27     """
28     typecheck( username, unicode
29              , balanced_customer_href, (unicode, None)
30               )
31 
32     if balanced_customer_href is None:
33         customer = balanced.Customer(meta={
34             'username': username,
35         }).save()
36         BALANCED_ACCOUNT = """\
37 
38                 UPDATE participants
39                    SET balanced_customer_href=%s
40                  WHERE username=%s
41 
42         """
43         db.run(BALANCED_ACCOUNT, (customer.href, username))
44     else:
45         customer = balanced.Customer.fetch(balanced_customer_href)
46     return customer
47 
48 
49 def associate(db, thing, username, balanced_customer_href, balanced_thing_uri):
50     """Given four unicodes, return a unicode.
51 
52     This function attempts to associate the credit card or bank account details
53     referenced by balanced_thing_uri with a Balanced Account. If it fails we
54     log and return a unicode describing the failure. Even for failure we keep
55     balanced_customer_href; we don't reset it to None/NULL. It's useful for
56     loading the previous (bad) info from Balanced in order to prepopulate the
57     form.
58 
59     """
60     typecheck( username, unicode
61                , balanced_customer_href, (unicode, None, balanced.Customer)
62                , balanced_thing_uri, unicode
63                , thing, unicode
64               )
65 
66     if isinstance(balanced_customer_href, balanced.Customer):
67         balanced_account = balanced_customer_href
68     else:
69         balanced_account = get_balanced_account( db
70                                                , username
71                                                , balanced_customer_href
72                                                 )
73     invalidate_on_balanced(thing, balanced_account.href)
74     SQL = "UPDATE participants SET last_%s_result=%%s WHERE username=%%s"
75     try:
76         if thing == "credit card":
77             SQL %= "bill"
78             obj = balanced.Card.fetch(balanced_thing_uri)
79             #add = balanced_account.add_card
80 
81         else:
82             assert thing == "bank account", thing # sanity check
83             SQL %= "ach"
84             obj = balanced.BankAccount.fetch(balanced_thing_uri)
85             #add = balanced_account.add_bank_account
86 
87         obj.associate_to_customer(balanced_account)
88     except balanced.exc.HTTPError as err:
89         error = err.message.message.decode('UTF-8')  # XXX UTF-8?
90     else:
91         error = ''
92     typecheck(error, unicode)
93 
94     db.run(SQL, (error, username))
95     return error
96 
97 
98 def invalidate_on_balanced(thing, balanced_customer_href):
99     """XXX Things in balanced cannot be deleted at the moment.
100 
101     Instead we mark all valid cards as invalid which will restrict against
102     anyone being able to issue charges against them in the future.
103 
104     See: https://github.com/balanced/balanced-api/issues/22
105 
106     """
107     assert thing in ("credit card", "bank account")
108     typecheck(balanced_customer_href, (str, unicode))
109 
110     customer = balanced.Customer.fetch(balanced_customer_href)
111     things = customer.cards if thing == "credit card" else customer.bank_accounts
112 
113     for _thing in things:
114         _thing.unstore()
115 
116 
117 def clear(db, thing, username, balanced_customer_href):
118     typecheck( thing, unicode
119              , username, unicode
120              , balanced_customer_href, (unicode, str)
121               )
122     assert thing in ("credit card", "bank account"), thing
123     invalidate_on_balanced(thing, balanced_customer_href)
124     CLEAR = """\
125 
126         UPDATE participants
127            SET last_%s_result=NULL
128          WHERE username=%%s
129 
130     """ % ("bill" if thing == "credit card" else "ach")
131     db.run(CLEAR, (username,))
132 
133 
134 def store_error(db, thing, username, msg):
135     typecheck(thing, unicode, username, unicode, msg, unicode)
136     assert thing in ("credit card", "bank account"), thing
137     ERROR = """\
138 
139         UPDATE participants
140            SET last_%s_result=%%s
141          WHERE username=%%s
142 
143     """ % ("bill" if thing == "credit card" else "ach")
144     db.run(ERROR, (msg, username))
145 
146 
147 class BalancedThing(object):
148     """Represent either a credit card or a bank account.
149     """
150 
151     thing_type = None           # either 'card' or 'bank_account'
152     keys_to_attr_paths = None   # set to a mapping in subclasses
153 
154     _customer = None    # underlying balanced.Customer object
155     _thing = None       # underlying balanced.{BankAccount,Card} object
156 
157     def __getitem__(self, key):
158         """Given a name, return a unicode.
159 
160         Allow subclasses to provide a flat set of keys, which, under the hood,
161         might be nested attributes and/or keys. The traversal path is relative
162         to _thing (not self!).
163 
164         """
165         attr_path = self.keys_to_attr_paths.get(key, key)
166 
167         out = None
168         if self._customer is not None and self._thing is not None:
169             out = self._thing
170             for val in attr_path.split('.'):
171                 if type(out) is dict:
172                     # this lets us reach into the meta dict
173                     out = out.get(val)
174                 else:
175                     try:
176                         out = getattr(out, val)
177                     except AttributeError:
178                         raise KeyError("{} not found".format(val))
179                 if out is None:
180                     break
181 
182         if out is None:
183             # Default to ''; see https://github.com/gittip/www.gittip.com/issues/2161.
184             out = ''
185 
186         return out
187 
188     def __init__(self, balanced_customer_href):
189         """Given a Balanced account_uri, load data from Balanced.
190         """
191         if balanced_customer_href is None:
192             return
193 
194         # XXX Indexing is borken. See:
195         # https://github.com/balanced/balanced-python/issues/10
196 
197         self._customer = balanced.Customer.fetch(balanced_customer_href)
198 
199         things = getattr(self._customer, self.thing_type+'s')\
200             .filter(is_valid=True).all()
201         nvalid = len(things)
202 
203         if nvalid == 0:
204             self._thing = None
205         elif nvalid == 1:
206             self._thing = things[0]
207         else:
208             msg = "%s has %d valid %ss"
209             msg %= (balanced_customer_href, len(things), self.thing_type)
210             raise RuntimeError(msg)
211 
212     @property
213     def is_setup(self):
214         return self._thing is not None
215 
216 
217 class BalancedCard(BalancedThing):
218     """This is a dict-like wrapper around a Balanced credit card.
219     """
220 
221     thing_type = 'card'
222 
223     keys_to_attr_paths = {
224         'id': 'customer.href',
225         'address_1': 'address.line1',
226         'address_2': 'meta.address_2',
227         'country': 'meta.country',
228         'city_town': 'meta.city_town',
229         'zip': 'address.postal_code',
230         # gittip is saving the state in the meta field
231         # for compatibility with legacy customers
232         'state': 'meta.region',
233         'last4': 'number',
234         'last_four': 'number',
235         'expiration_month': 'expiration_month',
236         'expiration_year': 'expiration_year',
237     }
238 
239 
240 class BalancedBankAccount(BalancedThing):
241     """This is a dict-like wrapper around a Balanced bank account.
242     """
243 
244     thing_type = 'bank_account'
245 
246     keys_to_attr_paths = {
247         'customer_href': 'customer.href',
248     }
249
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/gittip/billing/__init__.py b/gittip/billing/__init__.py
--- a/gittip/billing/__init__.py
+++ b/gittip/billing/__init__.py
@@ -22,6 +22,29 @@
 from aspen.utils import typecheck
 
 
+def update_receiving_amounts(db, tipper, error):
+    """Update receiving amounts of participants tipped by given tipper.
+    """
+    from gittip.models.participant import Participant
+    tipper = Participant.from_username(tipper)
+    if tipper.is_suspicious or tipper.last_bill_result == error:
+        return
+    if error == '':
+        op = '+'
+    else:
+        op = '-'
+    db.run("""
+        UPDATE participants
+           SET receiving = (receiving {0} amount)
+          FROM ( SELECT DISTINCT ON (tippee) tippee, amount
+                   FROM tips
+                  WHERE tipper=%(tipper)s
+               ORDER BY tippee, mtime DESC
+               ) foo
+         WHERE tippee = username;
+    """.format(op), dict(tipper=tipper))
+
+
 def get_balanced_account(db, username, balanced_customer_href):
     """Find or create a balanced.Account.
     """
@@ -92,6 +115,8 @@
     typecheck(error, unicode)
 
     db.run(SQL, (error, username))
+    if thing == "credit card":
+        update_receiving_amounts(db, username, error)
     return error
 
 
@@ -129,6 +154,8 @@
 
     """ % ("bill" if thing == "credit card" else "ach")
     db.run(CLEAR, (username,))
+    if thing == "credit card":
+        update_receiving_amounts(db, username, None)
 
 
 def store_error(db, thing, username, msg):
@@ -142,6 +169,8 @@
 
     """ % ("bill" if thing == "credit card" else "ach")
     db.run(ERROR, (msg, username))
+    if thing == "credit card":
+        update_receiving_amounts(db, username, msg)
 
 
 class BalancedThing(object):

{"golden_diff": "diff --git a/gittip/billing/__init__.py b/gittip/billing/__init__.py\n--- a/gittip/billing/__init__.py\n+++ b/gittip/billing/__init__.py\n@@ -22,6 +22,29 @@\n from aspen.utils import typecheck\n \n \n+def update_receiving_amounts(db, tipper, error):\n+    \"\"\"Update receiving amounts of participants tipped by given tipper.\n+    \"\"\"\n+    from gittip.models.participant import Participant\n+    tipper = Participant.from_username(tipper)\n+    if tipper.is_suspicious or tipper.last_bill_result == error:\n+        return\n+    if error == '':\n+        op = '+'\n+    else:\n+        op = '-'\n+    db.run(\"\"\"\n+        UPDATE participants\n+           SET receiving = (receiving {0} amount)\n+          FROM ( SELECT DISTINCT ON (tippee) tippee, amount\n+                   FROM tips\n+                  WHERE tipper=%(tipper)s\n+               ORDER BY tippee, mtime DESC\n+               ) foo\n+         WHERE tippee = username;\n+    \"\"\".format(op), dict(tipper=tipper))\n+\n+\n def get_balanced_account(db, username, balanced_customer_href):\n     \"\"\"Find or create a balanced.Account.\n     \"\"\"\n@@ -92,6 +115,8 @@\n     typecheck(error, unicode)\n \n     db.run(SQL, (error, username))\n+    if thing == \"credit card\":\n+        update_receiving_amounts(db, username, error)\n     return error\n \n \n@@ -129,6 +154,8 @@\n \n     \"\"\" % (\"bill\" if thing == \"credit card\" else \"ach\")\n     db.run(CLEAR, (username,))\n+    if thing == \"credit card\":\n+        update_receiving_amounts(db, username, None)\n \n \n def store_error(db, thing, username, msg):\n@@ -142,6 +169,8 @@\n \n     \"\"\" % (\"bill\" if thing == \"credit card\" else \"ach\")\n     db.run(ERROR, (msg, username))\n+    if thing == \"credit card\":\n+        update_receiving_amounts(db, username, msg)\n \n \n class BalancedThing(object):\n", "issue": "fix regression in receiving amounts\nStripe is giving to Bundler now, but Bundler's \"receiving\" doesn't reflect this, due to a regression with #2352. [IRC](https://botbot.me/freenode/gittip/2014-06-12/?msg=16141806&page=1)\n\ncc: @indirect @gdb\n\n", "before_files": [{"content": "\"\"\"This module encapsulates billing logic and db access.\n\nThere are three pieces of information for each participant related to billing:\n\n    balanced_customer_href\n        * NULL - This participant has never been billed.\n        * 'deadbeef' - This participant has had a Balanced account created for\n          them, either by adding a credit card or a bank account.\n    last_bill_result\n        * NULL - This participant has not had their credit card charged yet.\n        * '' - This participant has a working card.\n        * <message> - An error message.\n    last_ach_result\n        * NULL - This participant has not wired up a bank account yet.\n        * '' - This participant has a working bank account.\n        * <message> - An error message.\n\n\"\"\"\nfrom __future__ import unicode_literals\n\nimport balanced\nfrom aspen.utils import typecheck\n\n\ndef get_balanced_account(db, username, balanced_customer_href):\n    \"\"\"Find or create a balanced.Account.\n    \"\"\"\n    typecheck( username, unicode\n             , balanced_customer_href, (unicode, None)\n              )\n\n    if balanced_customer_href is None:\n        customer = balanced.Customer(meta={\n            'username': username,\n        }).save()\n        BALANCED_ACCOUNT = \"\"\"\\\n\n                UPDATE participants\n                   SET balanced_customer_href=%s\n                 WHERE username=%s\n\n        \"\"\"\n        db.run(BALANCED_ACCOUNT, (customer.href, username))\n    else:\n        customer = balanced.Customer.fetch(balanced_customer_href)\n    return customer\n\n\ndef associate(db, thing, username, balanced_customer_href, balanced_thing_uri):\n    \"\"\"Given four unicodes, return a unicode.\n\n    This function attempts to associate the credit card or bank account details\n    referenced by balanced_thing_uri with a Balanced Account. If it fails we\n    log and return a unicode describing the failure. Even for failure we keep\n    balanced_customer_href; we don't reset it to None/NULL. It's useful for\n    loading the previous (bad) info from Balanced in order to prepopulate the\n    form.\n\n    \"\"\"\n    typecheck( username, unicode\n               , balanced_customer_href, (unicode, None, balanced.Customer)\n               , balanced_thing_uri, unicode\n               , thing, unicode\n              )\n\n    if isinstance(balanced_customer_href, balanced.Customer):\n        balanced_account = balanced_customer_href\n    else:\n        balanced_account = get_balanced_account( db\n                                               , username\n                                               , balanced_customer_href\n                                                )\n    invalidate_on_balanced(thing, balanced_account.href)\n    SQL = \"UPDATE participants SET last_%s_result=%%s WHERE username=%%s\"\n    try:\n        if thing == \"credit card\":\n            SQL %= \"bill\"\n            obj = balanced.Card.fetch(balanced_thing_uri)\n            #add = balanced_account.add_card\n\n        else:\n            assert thing == \"bank account\", thing # sanity check\n            SQL %= \"ach\"\n            obj = balanced.BankAccount.fetch(balanced_thing_uri)\n            #add = balanced_account.add_bank_account\n\n        obj.associate_to_customer(balanced_account)\n    except balanced.exc.HTTPError as err:\n        error = err.message.message.decode('UTF-8')  # XXX UTF-8?\n    else:\n        error = ''\n    typecheck(error, unicode)\n\n    db.run(SQL, (error, username))\n    return error\n\n\ndef invalidate_on_balanced(thing, balanced_customer_href):\n    \"\"\"XXX Things in balanced cannot be deleted at the moment.\n\n    Instead we mark all valid cards as invalid which will restrict against\n    anyone being able to issue charges against them in the future.\n\n    See: https://github.com/balanced/balanced-api/issues/22\n\n    \"\"\"\n    assert thing in (\"credit card\", \"bank account\")\n    typecheck(balanced_customer_href, (str, unicode))\n\n    customer = balanced.Customer.fetch(balanced_customer_href)\n    things = customer.cards if thing == \"credit card\" else customer.bank_accounts\n\n    for _thing in things:\n        _thing.unstore()\n\n\ndef clear(db, thing, username, balanced_customer_href):\n    typecheck( thing, unicode\n             , username, unicode\n             , balanced_customer_href, (unicode, str)\n              )\n    assert thing in (\"credit card\", \"bank account\"), thing\n    invalidate_on_balanced(thing, balanced_customer_href)\n    CLEAR = \"\"\"\\\n\n        UPDATE participants\n           SET last_%s_result=NULL\n         WHERE username=%%s\n\n    \"\"\" % (\"bill\" if thing == \"credit card\" else \"ach\")\n    db.run(CLEAR, (username,))\n\n\ndef store_error(db, thing, username, msg):\n    typecheck(thing, unicode, username, unicode, msg, unicode)\n    assert thing in (\"credit card\", \"bank account\"), thing\n    ERROR = \"\"\"\\\n\n        UPDATE participants\n           SET last_%s_result=%%s\n         WHERE username=%%s\n\n    \"\"\" % (\"bill\" if thing == \"credit card\" else \"ach\")\n    db.run(ERROR, (msg, username))\n\n\nclass BalancedThing(object):\n    \"\"\"Represent either a credit card or a bank account.\n    \"\"\"\n\n    thing_type = None           # either 'card' or 'bank_account'\n    keys_to_attr_paths = None   # set to a mapping in subclasses\n\n    _customer = None    # underlying balanced.Customer object\n    _thing = None       # underlying balanced.{BankAccount,Card} object\n\n    def __getitem__(self, key):\n        \"\"\"Given a name, return a unicode.\n\n        Allow subclasses to provide a flat set of keys, which, under the hood,\n        might be nested attributes and/or keys. The traversal path is relative\n        to _thing (not self!).\n\n        \"\"\"\n        attr_path = self.keys_to_attr_paths.get(key, key)\n\n        out = None\n        if self._customer is not None and self._thing is not None:\n            out = self._thing\n            for val in attr_path.split('.'):\n                if type(out) is dict:\n                    # this lets us reach into the meta dict\n                    out = out.get(val)\n                else:\n                    try:\n                        out = getattr(out, val)\n                    except AttributeError:\n                        raise KeyError(\"{} not found\".format(val))\n                if out is None:\n                    break\n\n        if out is None:\n            # Default to ''; see https://github.com/gittip/www.gittip.com/issues/2161.\n            out = ''\n\n        return out\n\n    def __init__(self, balanced_customer_href):\n        \"\"\"Given a Balanced account_uri, load data from Balanced.\n        \"\"\"\n        if balanced_customer_href is None:\n            return\n\n        # XXX Indexing is borken. See:\n        # https://github.com/balanced/balanced-python/issues/10\n\n        self._customer = balanced.Customer.fetch(balanced_customer_href)\n\n        things = getattr(self._customer, self.thing_type+'s')\\\n            .filter(is_valid=True).all()\n        nvalid = len(things)\n\n        if nvalid == 0:\n            self._thing = None\n        elif nvalid == 1:\n            self._thing = things[0]\n        else:\n            msg = \"%s has %d valid %ss\"\n            msg %= (balanced_customer_href, len(things), self.thing_type)\n            raise RuntimeError(msg)\n\n    @property\n    def is_setup(self):\n        return self._thing is not None\n\n\nclass BalancedCard(BalancedThing):\n    \"\"\"This is a dict-like wrapper around a Balanced credit card.\n    \"\"\"\n\n    thing_type = 'card'\n\n    keys_to_attr_paths = {\n        'id': 'customer.href',\n        'address_1': 'address.line1',\n        'address_2': 'meta.address_2',\n        'country': 'meta.country',\n        'city_town': 'meta.city_town',\n        'zip': 'address.postal_code',\n        # gittip is saving the state in the meta field\n        # for compatibility with legacy customers\n        'state': 'meta.region',\n        'last4': 'number',\n        'last_four': 'number',\n        'expiration_month': 'expiration_month',\n        'expiration_year': 'expiration_year',\n    }\n\n\nclass BalancedBankAccount(BalancedThing):\n    \"\"\"This is a dict-like wrapper around a Balanced bank account.\n    \"\"\"\n\n    thing_type = 'bank_account'\n\n    keys_to_attr_paths = {\n        'customer_href': 'customer.href',\n    }\n", "path": "gittip/billing/__init__.py"}], "after_files": [{"content": "\"\"\"This module encapsulates billing logic and db access.\n\nThere are three pieces of information for each participant related to billing:\n\n    balanced_customer_href\n        * NULL - This participant has never been billed.\n        * 'deadbeef' - This participant has had a Balanced account created for\n          them, either by adding a credit card or a bank account.\n    last_bill_result\n        * NULL - This participant has not had their credit card charged yet.\n        * '' - This participant has a working card.\n        * <message> - An error message.\n    last_ach_result\n        * NULL - This participant has not wired up a bank account yet.\n        * '' - This participant has a working bank account.\n        * <message> - An error message.\n\n\"\"\"\nfrom __future__ import unicode_literals\n\nimport balanced\nfrom aspen.utils import typecheck\n\n\ndef update_receiving_amounts(db, tipper, error):\n    \"\"\"Update receiving amounts of participants tipped by given tipper.\n    \"\"\"\n    from gittip.models.participant import Participant\n    tipper = Participant.from_username(tipper)\n    if tipper.is_suspicious or tipper.last_bill_result == error:\n        return\n    if error == '':\n        op = '+'\n    else:\n        op = '-'\n    db.run(\"\"\"\n        UPDATE participants\n           SET receiving = (receiving {0} amount)\n          FROM ( SELECT DISTINCT ON (tippee) tippee, amount\n                   FROM tips\n                  WHERE tipper=%(tipper)s\n               ORDER BY tippee, mtime DESC\n               ) foo\n         WHERE tippee = username;\n    \"\"\".format(op), dict(tipper=tipper))\n\n\ndef get_balanced_account(db, username, balanced_customer_href):\n    \"\"\"Find or create a balanced.Account.\n    \"\"\"\n    typecheck( username, unicode\n             , balanced_customer_href, (unicode, None)\n              )\n\n    if balanced_customer_href is None:\n        customer = balanced.Customer(meta={\n            'username': username,\n        }).save()\n        BALANCED_ACCOUNT = \"\"\"\\\n\n                UPDATE participants\n                   SET balanced_customer_href=%s\n                 WHERE username=%s\n\n        \"\"\"\n        db.run(BALANCED_ACCOUNT, (customer.href, username))\n    else:\n        customer = balanced.Customer.fetch(balanced_customer_href)\n    return customer\n\n\ndef associate(db, thing, username, balanced_customer_href, balanced_thing_uri):\n    \"\"\"Given four unicodes, return a unicode.\n\n    This function attempts to associate the credit card or bank account details\n    referenced by balanced_thing_uri with a Balanced Account. If it fails we\n    log and return a unicode describing the failure. Even for failure we keep\n    balanced_customer_href; we don't reset it to None/NULL. It's useful for\n    loading the previous (bad) info from Balanced in order to prepopulate the\n    form.\n\n    \"\"\"\n    typecheck( username, unicode\n               , balanced_customer_href, (unicode, None, balanced.Customer)\n               , balanced_thing_uri, unicode\n               , thing, unicode\n              )\n\n    if isinstance(balanced_customer_href, balanced.Customer):\n        balanced_account = balanced_customer_href\n    else:\n        balanced_account = get_balanced_account( db\n                                               , username\n                                               , balanced_customer_href\n                                                )\n    invalidate_on_balanced(thing, balanced_account.href)\n    SQL = \"UPDATE participants SET last_%s_result=%%s WHERE username=%%s\"\n    try:\n        if thing == \"credit card\":\n            SQL %= \"bill\"\n            obj = balanced.Card.fetch(balanced_thing_uri)\n            #add = balanced_account.add_card\n\n        else:\n            assert thing == \"bank account\", thing # sanity check\n            SQL %= \"ach\"\n            obj = balanced.BankAccount.fetch(balanced_thing_uri)\n            #add = balanced_account.add_bank_account\n\n        obj.associate_to_customer(balanced_account)\n    except balanced.exc.HTTPError as err:\n        error = err.message.message.decode('UTF-8')  # XXX UTF-8?\n    else:\n        error = ''\n    typecheck(error, unicode)\n\n    db.run(SQL, (error, username))\n    if thing == \"credit card\":\n        update_receiving_amounts(db, username, error)\n    return error\n\n\ndef invalidate_on_balanced(thing, balanced_customer_href):\n    \"\"\"XXX Things in balanced cannot be deleted at the moment.\n\n    Instead we mark all valid cards as invalid which will restrict against\n    anyone being able to issue charges against them in the future.\n\n    See: https://github.com/balanced/balanced-api/issues/22\n\n    \"\"\"\n    assert thing in (\"credit card\", \"bank account\")\n    typecheck(balanced_customer_href, (str, unicode))\n\n    customer = balanced.Customer.fetch(balanced_customer_href)\n    things = customer.cards if thing == \"credit card\" else customer.bank_accounts\n\n    for _thing in things:\n        _thing.unstore()\n\n\ndef clear(db, thing, username, balanced_customer_href):\n    typecheck( thing, unicode\n             , username, unicode\n             , balanced_customer_href, (unicode, str)\n              )\n    assert thing in (\"credit card\", \"bank account\"), thing\n    invalidate_on_balanced(thing, balanced_customer_href)\n    CLEAR = \"\"\"\\\n\n        UPDATE participants\n           SET last_%s_result=NULL\n         WHERE username=%%s\n\n    \"\"\" % (\"bill\" if thing == \"credit card\" else \"ach\")\n    db.run(CLEAR, (username,))\n    if thing == \"credit card\":\n        update_receiving_amounts(db, username, None)\n\n\ndef store_error(db, thing, username, msg):\n    typecheck(thing, unicode, username, unicode, msg, unicode)\n    assert thing in (\"credit card\", \"bank account\"), thing\n    ERROR = \"\"\"\\\n\n        UPDATE participants\n           SET last_%s_result=%%s\n         WHERE username=%%s\n\n    \"\"\" % (\"bill\" if thing == \"credit card\" else \"ach\")\n    db.run(ERROR, (msg, username))\n    if thing == \"credit card\":\n        update_receiving_amounts(db, username, msg)\n\n\nclass BalancedThing(object):\n    \"\"\"Represent either a credit card or a bank account.\n    \"\"\"\n\n    thing_type = None           # either 'card' or 'bank_account'\n    keys_to_attr_paths = None   # set to a mapping in subclasses\n\n    _customer = None    # underlying balanced.Customer object\n    _thing = None       # underlying balanced.{BankAccount,Card} object\n\n    def __getitem__(self, key):\n        \"\"\"Given a name, return a unicode.\n\n        Allow subclasses to provide a flat set of keys, which, under the hood,\n        might be nested attributes and/or keys. The traversal path is relative\n        to _thing (not self!).\n\n        \"\"\"\n        attr_path = self.keys_to_attr_paths.get(key, key)\n\n        out = None\n        if self._customer is not None and self._thing is not None:\n            out = self._thing\n            for val in attr_path.split('.'):\n                if type(out) is dict:\n                    # this lets us reach into the meta dict\n                    out = out.get(val)\n                else:\n                    try:\n                        out = getattr(out, val)\n                    except AttributeError:\n                        raise KeyError(\"{} not found\".format(val))\n                if out is None:\n                    break\n\n        if out is None:\n            # Default to ''; see https://github.com/gittip/www.gittip.com/issues/2161.\n            out = ''\n\n        return out\n\n    def __init__(self, balanced_customer_href):\n        \"\"\"Given a Balanced account_uri, load data from Balanced.\n        \"\"\"\n        if balanced_customer_href is None:\n            return\n\n        # XXX Indexing is borken. See:\n        # https://github.com/balanced/balanced-python/issues/10\n\n        self._customer = balanced.Customer.fetch(balanced_customer_href)\n\n        things = getattr(self._customer, self.thing_type+'s')\\\n            .filter(is_valid=True).all()\n        nvalid = len(things)\n\n        if nvalid == 0:\n            self._thing = None\n        elif nvalid == 1:\n            self._thing = things[0]\n        else:\n            msg = \"%s has %d valid %ss\"\n            msg %= (balanced_customer_href, len(things), self.thing_type)\n            raise RuntimeError(msg)\n\n    @property\n    def is_setup(self):\n        return self._thing is not None\n\n\nclass BalancedCard(BalancedThing):\n    \"\"\"This is a dict-like wrapper around a Balanced credit card.\n    \"\"\"\n\n    thing_type = 'card'\n\n    keys_to_attr_paths = {\n        'id': 'customer.href',\n        'address_1': 'address.line1',\n        'address_2': 'meta.address_2',\n        'country': 'meta.country',\n        'city_town': 'meta.city_town',\n        'zip': 'address.postal_code',\n        # gittip is saving the state in the meta field\n        # for compatibility with legacy customers\n        'state': 'meta.region',\n        'last4': 'number',\n        'last_four': 'number',\n        'expiration_month': 'expiration_month',\n        'expiration_year': 'expiration_year',\n    }\n\n\nclass BalancedBankAccount(BalancedThing):\n    \"\"\"This is a dict-like wrapper around a Balanced bank account.\n    \"\"\"\n\n    thing_type = 'bank_account'\n\n    keys_to_attr_paths = {\n        'customer_href': 'customer.href',\n    }\n", "path": "gittip/billing/__init__.py"}]}