We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
500 from credit card page
Reticketing from #2155.

https://app.getsentry.com/gittip/gittip/group/16333701/ (login required)

```
TypeError: 'NoneType' object has no attribute '__getitem__'

Stacktrace (most recent call last):

  File "site-packages/algorithm.py", line 288, in run
    new_state = function(**deps.as_kwargs)
  File "aspen/algorithms/website.py", line 88, in get_response_for_resource
    return {'response': resource.respond(request)}
  File "aspen/resources/dynamic_resource.py", line 68, in respond
    response = self.get_response(context)
  File "aspen/resources/negotiated_resource.py", line 98, in get_response
    response.body = render(context)
  File "aspen/renderers/__init__.py", line 99, in __call__
    return self.render_content(context)
  File "site-packages/aspen_jinja2_renderer.py", line 66, in render_content
    return self.compiled.render(context).encode(charset)
  File "jinja2/environment.py", line 969, in render
    return self.environment.handle_exception(exc_info, True)
  File "jinja2/environment.py", line 742, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/app/www/credit-card.html.spt", line 1, in top-level template code
    import traceback
  File "/app/templates/base.html", line 54, in top-level template code
    {% block killbox %}
  File "/app/templates/base.html", line 56, in block "killbox"
    {% block box %}{% endblock %}
  File "/app/www/credit-card.html.spt", line 56, in block "box"
    Gittip.payments.cc.init("{{ balanced.Marketplace.my_marketplace.uri }}", "{{ user.participant.username }}");
```


--- 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 import stripe
23 from aspen.utils import typecheck
24 
25 
26 def get_balanced_account(db, username, balanced_customer_href):
27     """Find or create a balanced.Account.
28     """
29     typecheck( username, unicode
30              , balanced_customer_href, (unicode, None)
31               )
32 
33     if balanced_customer_href is None:
34         customer = balanced.Customer(meta={
35             'username': username,
36         }).save()
37         BALANCED_ACCOUNT = """\
38 
39                 UPDATE participants
40                    SET balanced_customer_href=%s
41                  WHERE username=%s
42 
43         """
44         db.run(BALANCED_ACCOUNT, (customer.href, username))
45     else:
46         customer = balanced.Customer.fetch(balanced_customer_href)
47     return customer
48 
49 
50 def associate(db, thing, username, balanced_customer_href, balanced_thing_uri):
51     """Given four unicodes, return a unicode.
52 
53     This function attempts to associate the credit card or bank account details
54     referenced by balanced_thing_uri with a Balanced Account. If it fails we
55     log and return a unicode describing the failure. Even for failure we keep
56     balanced_customer_href; we don't reset it to None/NULL. It's useful for
57     loading the previous (bad) info from Balanced in order to prepopulate the
58     form.
59 
60     """
61     typecheck( username, unicode
62                , balanced_customer_href, (unicode, None, balanced.Customer)
63                , balanced_thing_uri, unicode
64                , thing, unicode
65               )
66 
67     if isinstance(balanced_customer_href, balanced.Customer):
68         balanced_account = balanced_customer_href
69     else:
70         balanced_account = get_balanced_account( db
71                                                , username
72                                                , balanced_customer_href
73                                                 )
74     invalidate_on_balanced(thing, balanced_account.href)
75     SQL = "UPDATE participants SET last_%s_result=%%s WHERE username=%%s"
76     try:
77         if thing == "credit card":
78             SQL %= "bill"
79             obj = balanced.Card.fetch(balanced_thing_uri)
80             #add = balanced_account.add_card
81 
82         else:
83             assert thing == "bank account", thing # sanity check
84             SQL %= "ach"
85             obj = balanced.BankAccount.fetch(balanced_thing_uri)
86             #add = balanced_account.add_bank_account
87 
88         obj.associate_to_customer(balanced_account)
89     except balanced.exc.HTTPError as err:
90         error = err.message.message.decode('UTF-8')  # XXX UTF-8?
91     else:
92         error = ''
93     typecheck(error, unicode)
94 
95     db.run(SQL, (error, username))
96     return error
97 
98 
99 def invalidate_on_balanced(thing, balanced_customer_href):
100     """XXX Things in balanced cannot be deleted at the moment.
101 
102     Instead we mark all valid cards as invalid which will restrict against
103     anyone being able to issue charges against them in the future.
104 
105     See: https://github.com/balanced/balanced-api/issues/22
106 
107     """
108     assert thing in ("credit card", "bank account")
109     typecheck(balanced_customer_href, (str, unicode))
110 
111     customer = balanced.Customer.fetch(balanced_customer_href)
112     things = customer.cards if thing == "credit card" else customer.bank_accounts
113 
114     for _thing in things:
115         _thing.unstore()
116 
117 
118 def clear(db, thing, username, balanced_customer_href):
119     typecheck( thing, unicode
120              , username, unicode
121              , balanced_customer_href, (unicode, str)
122               )
123     assert thing in ("credit card", "bank account"), thing
124     invalidate_on_balanced(thing, balanced_customer_href)
125     CLEAR = """\
126 
127         UPDATE participants
128            SET last_%s_result=NULL
129          WHERE username=%%s
130 
131     """ % ("bill" if thing == "credit card" else "ach")
132     db.run(CLEAR, (username,))
133 
134 
135 def store_error(db, thing, username, msg):
136     typecheck(thing, unicode, username, unicode, msg, unicode)
137     assert thing in ("credit card", "bank account"), thing
138     ERROR = """\
139 
140         UPDATE participants
141            SET last_%s_result=%%s
142          WHERE username=%%s
143 
144     """ % ("bill" if thing == "credit card" else "ach")
145     db.run(ERROR, (msg, username))
146 
147 
148 # Card
149 # ====
150 # While we're migrating data we need to support loading data from both Stripe
151 # and Balanced.
152 
153 class StripeCard(object):
154     """This is a dict-like wrapper around a Stripe PaymentMethod.
155     """
156 
157     _customer = None  # underlying stripe.Customer object
158 
159     def __init__(self, stripe_customer_id):
160         """Given a Stripe customer id, load data from Stripe.
161         """
162         if stripe_customer_id is not None:
163             self._customer = stripe.Customer.retrieve(stripe_customer_id)
164 
165     def _get(self, name, default=""):
166         """Given a name, return a string.
167         """
168         out = ""
169         if self._customer is not None:
170             out = self._customer.get('active_card', {}).get(name, "")
171             if out is None:
172                 out = default
173         return out
174 
175     def __getitem__(self, name):
176         """Given a name, return a string.
177         """
178         if name == 'id':
179             out = self._customer.id if self._customer is not None else None
180         elif name == 'last4':
181             out = self._get('last4')
182             if out:
183                 out = "************" + out
184         else:
185             name = { 'address_1': 'address_line1'
186                    , 'address_2': 'address_line2'
187                    , 'state': 'address_state'
188                    , 'zip': 'address_zip'
189                     }.get(name, name)
190             out = self._get(name)
191         return out
192 
193 
194 class BalancedThing(object):
195     """Represent either a credit card or a bank account.
196     """
197 
198     thing_type = None           # either 'card' or 'bank_account'
199     keys_to_attr_paths = None   # set to a mapping in subclasses
200 
201     _customer = None    # underlying balanced.Customer object
202     _thing = None       # underlying balanced.{BankAccount,Card} object
203 
204     def __getitem__(self, key):
205         """Given a name, return a unicode.
206 
207         Allow subclasses to provide a flat set of keys, which, under the hood,
208         might be nested attributes and/or keys. The traversal path is relative
209         to _thing (not self!).
210 
211         """
212         attr_path = self.keys_to_attr_paths.get(key, key)
213 
214         out = None
215         if self._customer is not None and self._thing is not None:
216             out = self._thing
217             for val in attr_path.split('.'):
218                 if type(out) is dict:
219                     # this lets us reach into the meta dict
220                     out = out.get(val)
221                 else:
222                     try:
223                         out = getattr(out, val)
224                     except AttributeError:
225                         raise KeyError("{} not found".format(val))
226                 if out is None:
227                     break
228         return out
229 
230     def __init__(self, balanced_customer_href):
231         """Given a Balanced account_uri, load data from Balanced.
232         """
233         if balanced_customer_href is None:
234             return
235 
236         # XXX Indexing is borken. See:
237         # https://github.com/balanced/balanced-python/issues/10
238 
239         self._customer = balanced.Customer.fetch(balanced_customer_href)
240 
241         things = getattr(self._customer, self.thing_type+'s')\
242             .filter(is_valid=True).all()
243         nvalid = len(things)
244 
245         if nvalid == 0:
246             self._thing = None
247         elif nvalid == 1:
248             self._thing = things[0]
249         else:
250             msg = "%s has %d valid %ss"
251             msg %= (balanced_customer_href, len(things), self.thing_type)
252             raise RuntimeError(msg)
253 
254     @property
255     def is_setup(self):
256         return self._thing is not None
257 
258 
259 class BalancedCard(BalancedThing):
260     """This is a dict-like wrapper around a Balanced credit card.
261     """
262 
263     thing_type = 'card'
264 
265     keys_to_attr_paths = {
266         'id': 'customer.href',
267         'address_1': 'address.line1',
268         'address_2': 'meta.address_2',
269         'country': 'meta.country',
270         'city_town': 'meta.city_town',
271         'zip': 'address.postal_code',
272         # gittip is saving the state in the meta field
273         # for compatibility with legacy customers
274         'state': 'meta.region',
275         'last4': 'number',
276         'last_four': 'number',
277     }
278 
279 
280 class BalancedBankAccount(BalancedThing):
281     """This is a dict-like wrapper around a Balanced bank account.
282     """
283 
284     thing_type = 'bank_account'
285 
286     keys_to_attr_paths = {
287         'customer_href': 'customer.href',
288     }
289
```
--- 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
@@ -225,6 +225,11 @@
                         raise KeyError("{} not found".format(val))
                 if out is None:
                     break
+
+        if out is None:
+            # Default to ''; see https://github.com/gittip/www.gittip.com/issues/2161.
+            out = ''
+
         return out
 
     def __init__(self, balanced_customer_href):

{"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@@ -225,6 +225,11 @@\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", "issue": "500 from credit card page\nReticketing from #2155.\n\nhttps://app.getsentry.com/gittip/gittip/group/16333701/ (login required)\n\n```\nTypeError: 'NoneType' object has no attribute '__getitem__'\n\nStacktrace (most recent call last):\n\n  File \"site-packages/algorithm.py\", line 288, in run\n    new_state = function(**deps.as_kwargs)\n  File \"aspen/algorithms/website.py\", line 88, in get_response_for_resource\n    return {'response': resource.respond(request)}\n  File \"aspen/resources/dynamic_resource.py\", line 68, in respond\n    response = self.get_response(context)\n  File \"aspen/resources/negotiated_resource.py\", line 98, in get_response\n    response.body = render(context)\n  File \"aspen/renderers/__init__.py\", line 99, in __call__\n    return self.render_content(context)\n  File \"site-packages/aspen_jinja2_renderer.py\", line 66, in render_content\n    return self.compiled.render(context).encode(charset)\n  File \"jinja2/environment.py\", line 969, in render\n    return self.environment.handle_exception(exc_info, True)\n  File \"jinja2/environment.py\", line 742, in handle_exception\n    reraise(exc_type, exc_value, tb)\n  File \"/app/www/credit-card.html.spt\", line 1, in top-level template code\n    import traceback\n  File \"/app/templates/base.html\", line 54, in top-level template code\n    {% block killbox %}\n  File \"/app/templates/base.html\", line 56, in block \"killbox\"\n    {% block box %}{% endblock %}\n  File \"/app/www/credit-card.html.spt\", line 56, in block \"box\"\n    Gittip.payments.cc.init(\"{{ balanced.Marketplace.my_marketplace.uri }}\", \"{{ user.participant.username }}\");\n```\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\nimport stripe\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\n# Card\n# ====\n# While we're migrating data we need to support loading data from both Stripe\n# and Balanced.\n\nclass StripeCard(object):\n    \"\"\"This is a dict-like wrapper around a Stripe PaymentMethod.\n    \"\"\"\n\n    _customer = None  # underlying stripe.Customer object\n\n    def __init__(self, stripe_customer_id):\n        \"\"\"Given a Stripe customer id, load data from Stripe.\n        \"\"\"\n        if stripe_customer_id is not None:\n            self._customer = stripe.Customer.retrieve(stripe_customer_id)\n\n    def _get(self, name, default=\"\"):\n        \"\"\"Given a name, return a string.\n        \"\"\"\n        out = \"\"\n        if self._customer is not None:\n            out = self._customer.get('active_card', {}).get(name, \"\")\n            if out is None:\n                out = default\n        return out\n\n    def __getitem__(self, name):\n        \"\"\"Given a name, return a string.\n        \"\"\"\n        if name == 'id':\n            out = self._customer.id if self._customer is not None else None\n        elif name == 'last4':\n            out = self._get('last4')\n            if out:\n                out = \"************\" + out\n        else:\n            name = { 'address_1': 'address_line1'\n                   , 'address_2': 'address_line2'\n                   , 'state': 'address_state'\n                   , 'zip': 'address_zip'\n                    }.get(name, name)\n            out = self._get(name)\n        return out\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        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    }\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\nimport stripe\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\n# Card\n# ====\n# While we're migrating data we need to support loading data from both Stripe\n# and Balanced.\n\nclass StripeCard(object):\n    \"\"\"This is a dict-like wrapper around a Stripe PaymentMethod.\n    \"\"\"\n\n    _customer = None  # underlying stripe.Customer object\n\n    def __init__(self, stripe_customer_id):\n        \"\"\"Given a Stripe customer id, load data from Stripe.\n        \"\"\"\n        if stripe_customer_id is not None:\n            self._customer = stripe.Customer.retrieve(stripe_customer_id)\n\n    def _get(self, name, default=\"\"):\n        \"\"\"Given a name, return a string.\n        \"\"\"\n        out = \"\"\n        if self._customer is not None:\n            out = self._customer.get('active_card', {}).get(name, \"\")\n            if out is None:\n                out = default\n        return out\n\n    def __getitem__(self, name):\n        \"\"\"Given a name, return a string.\n        \"\"\"\n        if name == 'id':\n            out = self._customer.id if self._customer is not None else None\n        elif name == 'last4':\n            out = self._get('last4')\n            if out:\n                out = \"************\" + out\n        else:\n            name = { 'address_1': 'address_line1'\n                   , 'address_2': 'address_line2'\n                   , 'state': 'address_state'\n                   , 'zip': 'address_zip'\n                    }.get(name, name)\n            out = self._get(name)\n        return out\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    }\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"}]}