We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Simplify mailing list opt-in even further
In #4651 we dropped back to two mailing list options, quarterly and yearly. However, as I think through the way #BackTheStack is gonna wanna play out, we are going to want to send multiple updates over the course of the campaign. That's not consistent with a "quarterly" or "yearly" newsletter. I think our best bet is to just have a simple "Yes/No" on the mailing list.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `gratipay/models/payment_for_open_source.py`
Content:
```
1 # -*- coding: utf-8 -*-
2 from __future__ import absolute_import, division, print_function, unicode_literals
3 
4 import gratipay
5 from uuid import uuid4
6 from postgres.orm import Model
7 
8 
9 class PaymentForOpenSource(Model):
10 
11     typname = "payments_for_open_source"
12 
13     def __repr__(self):
14         return '<PaymentForOpenSource: {}>'.format(repr(self.amount))
15 
16 
17     @property
18     def succeeded(self):
19         return self.braintree_result_message == ''
20 
21 
22     @property
23     def invoice_url(self):
24         if not self.succeeded:
25             return None
26         return '{}/browse/payments/{}/invoice.html'.format(gratipay.base_url, self.uuid)
27 
28 
29     @classmethod
30     def from_uuid(cls, uuid, cursor=None):
31         """Take a uuid and return an object.
32         """
33         return (cursor or cls.db).one("""
34             SELECT pfos.*::payments_for_open_source
35               FROM payments_for_open_source pfos
36              WHERE uuid = %s
37         """, (uuid,))
38 
39 
40     @classmethod
41     def insert(cls, amount, grateful_for, name, follow_up, email_address,
42                promotion_name, promotion_url, promotion_twitter, promotion_message,
43                cursor=None):
44         """Take baseline info and insert into the database.
45         """
46         uuid = uuid4().hex
47         return (cursor or cls.db).one("""
48             INSERT INTO payments_for_open_source
49                         (uuid, amount, grateful_for, name, follow_up, email_address,
50                          promotion_name, promotion_url, promotion_twitter, promotion_message)
51                  VALUES (%s, %s, %s, %s, %s, %s,
52                          %s, %s, %s, %s)
53               RETURNING payments_for_open_source.*::payments_for_open_source
54         """, (uuid, amount, grateful_for, name, follow_up, email_address,
55               promotion_name, promotion_url, promotion_twitter, promotion_message))
56 
57 
58     def process_result(self, result):
59         """Take a Braintree API result and update the database.
60         """
61         result_message = '' if result.is_success else result.message
62         transaction_id = None
63         if result.transaction:
64             transaction_id = result.transaction.id
65 
66             # Verify that Braintree is sending us the right payload.
67             # TODO This is hard to test and it should be a pretty tight guarantee,
68             # so I am commenting out for now. :(
69             #pfos_uuid = result.transaction.custom_fields['pfos_uuid']
70             #assert pfos_uuid == self.uuid, (pfos_uuid, transaction_id)
71 
72         self.db.run("""
73             UPDATE payments_for_open_source
74                SET braintree_result_message=%s
75                  , braintree_transaction_id=%s
76              WHERE uuid=%s
77         """, (result_message, transaction_id, self.uuid))
78         self.set_attributes( braintree_result_message=result_message
79                            , braintree_transaction_id=transaction_id
80                             )
81
```

Path: `gratipay/homepage.py`
Content:
```
1 # -*- coding: utf-8 -*-
2 """This is the Python library behind gratipay.com.
3 """
4 from __future__ import absolute_import, division, print_function, unicode_literals
5 
6 from gratipay import utils
7 from gratipay.models.payment_for_open_source import PaymentForOpenSource
8 
9 
10 kB = 2**10
11 
12 
13 def _parse(raw):
14     """Given a POST request.body, return (parsed<dict>, errors<list>).
15     """
16 
17     errors = []
18     x = lambda f: raw[f].strip()  # KeyError -> 400
19 
20     # payment info
21     amount = x('amount')
22     if (not amount.isdigit()) or (int(amount) < 10):
23         errors.append('amount')
24         amount = ''.join(x for x in amount.split('.')[0] if x.isdigit())
25 
26     payment_method_nonce = x('payment_method_nonce')
27     if len(payment_method_nonce) > 36:
28         errors.append('payment_method_nonce')
29         payment_method_nonce = ''
30 
31     # ecosystems/packages
32     grateful_for = x('grateful_for')
33     if len(grateful_for) > 16*kB:
34         grateful_for = grateful_for[:16*kB]
35         errors.append('grateful_for')
36 
37     # contact info
38     name = x('name')
39     if len(name) > 255:
40         name = name[:255]
41         errors.append('name')
42 
43     email_address = x('email_address')
44     if email_address and not utils.is_valid_email_address(email_address):
45         email_address = email_address[:255]
46         errors.append('email_address')
47 
48     follow_up = x('follow_up')
49     if follow_up not in ('quarterly', 'yearly', 'never'):
50         follow_up = 'quarterly'
51         errors.append('follow_up')
52 
53     # promo fields
54     promotion_name = x('promotion_name')
55     if len(promotion_name) > 32:
56         promotion_name = promotion_name[:32]
57         errors.append('promotion_name')
58 
59     promotion_url = x('promotion_url')
60     is_link = lambda x: (x.startswith('http://') or x.startswith('https://')) and '.' in x
61     if len(promotion_url) > 255 or (promotion_url and not is_link(promotion_url)):
62         promotion_url = promotion_url[:255]
63         errors.append('promotion_url')
64 
65     promotion_twitter = x('promotion_twitter')
66     if len(promotion_twitter) > 32:
67         promotion_twitter = promotion_twitter[:32]
68         # TODO What are Twitter's rules?
69         errors.append('promotion_twitter')
70 
71     promotion_message = x('promotion_message')
72     if len(promotion_message) > 128:
73         promotion_message = promotion_message[:128]
74         errors.append('promotion_message')
75 
76     parsed = { 'amount': amount
77              , 'payment_method_nonce': payment_method_nonce
78              , 'grateful_for': grateful_for
79              , 'name': name
80              , 'email_address': email_address
81              , 'follow_up': follow_up
82              , 'promotion_name': promotion_name
83              , 'promotion_url': promotion_url
84              , 'promotion_twitter': promotion_twitter
85              , 'promotion_message': promotion_message
86               }
87     return parsed, errors
88 
89 
90 def _store(parsed):
91     return PaymentForOpenSource.insert(**parsed)
92 
93 
94 def _charge(app, pfos, nonce):
95     params = { 'amount': pfos.amount
96              , 'payment_method_nonce': nonce
97              , 'options': {'submit_for_settlement': True}
98              , 'custom_fields': {'pfos_uuid': pfos.uuid}
99               }
100     result = app.pfos_card_charger.charge(params)
101     pfos.process_result(result)
102 
103 
104 def _send(app, pfos):
105     app.email_queue.put( to=None
106                        , template='paid-for-open-source'
107                        , email=pfos.email_address
108                        , amount=pfos.amount
109                        , invoice_url=pfos.invoice_url
110                         )
111 
112 
113 def pay_for_open_source(app, raw):
114     parsed, errors = _parse(raw)
115     out = {'errors': errors, 'invoice_url': None}
116     if not errors:
117         payment_method_nonce = parsed.pop('payment_method_nonce')
118         pfos = _store(parsed)
119         _charge(app, pfos, payment_method_nonce)
120         if pfos.succeeded:
121             out['invoice_url'] = pfos.invoice_url
122             if pfos.email_address:
123                 _send(app, pfos)
124         else:
125             out['errors'].append('charging')
126     return out
127
```
--- 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/gratipay/homepage.py b/gratipay/homepage.py
--- a/gratipay/homepage.py
+++ b/gratipay/homepage.py
@@ -45,10 +45,10 @@
         email_address = email_address[:255]
         errors.append('email_address')
 
-    follow_up = x('follow_up')
-    if follow_up not in ('quarterly', 'yearly', 'never'):
-        follow_up = 'quarterly'
-        errors.append('follow_up')
+    on_mailing_list = x('on_mailing_list')
+    if on_mailing_list not in ('yes', 'no'):
+        on_mailing_list = 'yes'
+        errors.append('on_mailing_list')
 
     # promo fields
     promotion_name = x('promotion_name')
@@ -78,7 +78,7 @@
              , 'grateful_for': grateful_for
              , 'name': name
              , 'email_address': email_address
-             , 'follow_up': follow_up
+             , 'on_mailing_list': on_mailing_list
              , 'promotion_name': promotion_name
              , 'promotion_url': promotion_url
              , 'promotion_twitter': promotion_twitter
diff --git a/gratipay/models/payment_for_open_source.py b/gratipay/models/payment_for_open_source.py
--- a/gratipay/models/payment_for_open_source.py
+++ b/gratipay/models/payment_for_open_source.py
@@ -38,20 +38,21 @@
 
 
     @classmethod
-    def insert(cls, amount, grateful_for, name, follow_up, email_address,
+    def insert(cls, amount, grateful_for, name, on_mailing_list, email_address,
                promotion_name, promotion_url, promotion_twitter, promotion_message,
                cursor=None):
         """Take baseline info and insert into the database.
         """
         uuid = uuid4().hex
+        on_mailing_list = on_mailing_list == 'yes'
         return (cursor or cls.db).one("""
             INSERT INTO payments_for_open_source
-                        (uuid, amount, grateful_for, name, follow_up, email_address,
+                        (uuid, amount, grateful_for, name, on_mailing_list, email_address,
                          promotion_name, promotion_url, promotion_twitter, promotion_message)
                  VALUES (%s, %s, %s, %s, %s, %s,
                          %s, %s, %s, %s)
               RETURNING payments_for_open_source.*::payments_for_open_source
-        """, (uuid, amount, grateful_for, name, follow_up, email_address,
+        """, (uuid, amount, grateful_for, name, on_mailing_list, email_address,
               promotion_name, promotion_url, promotion_twitter, promotion_message))

{"golden_diff": "diff --git a/gratipay/homepage.py b/gratipay/homepage.py\n--- a/gratipay/homepage.py\n+++ b/gratipay/homepage.py\n@@ -45,10 +45,10 @@\n         email_address = email_address[:255]\n         errors.append('email_address')\n \n-    follow_up = x('follow_up')\n-    if follow_up not in ('quarterly', 'yearly', 'never'):\n-        follow_up = 'quarterly'\n-        errors.append('follow_up')\n+    on_mailing_list = x('on_mailing_list')\n+    if on_mailing_list not in ('yes', 'no'):\n+        on_mailing_list = 'yes'\n+        errors.append('on_mailing_list')\n \n     # promo fields\n     promotion_name = x('promotion_name')\n@@ -78,7 +78,7 @@\n              , 'grateful_for': grateful_for\n              , 'name': name\n              , 'email_address': email_address\n-             , 'follow_up': follow_up\n+             , 'on_mailing_list': on_mailing_list\n              , 'promotion_name': promotion_name\n              , 'promotion_url': promotion_url\n              , 'promotion_twitter': promotion_twitter\ndiff --git a/gratipay/models/payment_for_open_source.py b/gratipay/models/payment_for_open_source.py\n--- a/gratipay/models/payment_for_open_source.py\n+++ b/gratipay/models/payment_for_open_source.py\n@@ -38,20 +38,21 @@\n \n \n     @classmethod\n-    def insert(cls, amount, grateful_for, name, follow_up, email_address,\n+    def insert(cls, amount, grateful_for, name, on_mailing_list, email_address,\n                promotion_name, promotion_url, promotion_twitter, promotion_message,\n                cursor=None):\n         \"\"\"Take baseline info and insert into the database.\n         \"\"\"\n         uuid = uuid4().hex\n+        on_mailing_list = on_mailing_list == 'yes'\n         return (cursor or cls.db).one(\"\"\"\n             INSERT INTO payments_for_open_source\n-                        (uuid, amount, grateful_for, name, follow_up, email_address,\n+                        (uuid, amount, grateful_for, name, on_mailing_list, email_address,\n                          promotion_name, promotion_url, promotion_twitter, promotion_message)\n                  VALUES (%s, %s, %s, %s, %s, %s,\n                          %s, %s, %s, %s)\n               RETURNING payments_for_open_source.*::payments_for_open_source\n-        \"\"\", (uuid, amount, grateful_for, name, follow_up, email_address,\n+        \"\"\", (uuid, amount, grateful_for, name, on_mailing_list, email_address,\n               promotion_name, promotion_url, promotion_twitter, promotion_message))\n", "issue": "Simplify mailing list opt-in even further\nIn #4651 we dropped back to two mailing list options, quarterly and yearly. However, as I think through the way #BackTheStack is gonna wanna play out, we are going to want to send multiple updates over the course of the campaign. That's not consistent with a \"quarterly\" or \"yearly\" newsletter. I think our best bet is to just have a simple \"Yes/No\" on the mailing list.\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\nfrom __future__ import absolute_import, division, print_function, unicode_literals\n\nimport gratipay\nfrom uuid import uuid4\nfrom postgres.orm import Model\n\n\nclass PaymentForOpenSource(Model):\n\n    typname = \"payments_for_open_source\"\n\n    def __repr__(self):\n        return '<PaymentForOpenSource: {}>'.format(repr(self.amount))\n\n\n    @property\n    def succeeded(self):\n        return self.braintree_result_message == ''\n\n\n    @property\n    def invoice_url(self):\n        if not self.succeeded:\n            return None\n        return '{}/browse/payments/{}/invoice.html'.format(gratipay.base_url, self.uuid)\n\n\n    @classmethod\n    def from_uuid(cls, uuid, cursor=None):\n        \"\"\"Take a uuid and return an object.\n        \"\"\"\n        return (cursor or cls.db).one(\"\"\"\n            SELECT pfos.*::payments_for_open_source\n              FROM payments_for_open_source pfos\n             WHERE uuid = %s\n        \"\"\", (uuid,))\n\n\n    @classmethod\n    def insert(cls, amount, grateful_for, name, follow_up, email_address,\n               promotion_name, promotion_url, promotion_twitter, promotion_message,\n               cursor=None):\n        \"\"\"Take baseline info and insert into the database.\n        \"\"\"\n        uuid = uuid4().hex\n        return (cursor or cls.db).one(\"\"\"\n            INSERT INTO payments_for_open_source\n                        (uuid, amount, grateful_for, name, follow_up, email_address,\n                         promotion_name, promotion_url, promotion_twitter, promotion_message)\n                 VALUES (%s, %s, %s, %s, %s, %s,\n                         %s, %s, %s, %s)\n              RETURNING payments_for_open_source.*::payments_for_open_source\n        \"\"\", (uuid, amount, grateful_for, name, follow_up, email_address,\n              promotion_name, promotion_url, promotion_twitter, promotion_message))\n\n\n    def process_result(self, result):\n        \"\"\"Take a Braintree API result and update the database.\n        \"\"\"\n        result_message = '' if result.is_success else result.message\n        transaction_id = None\n        if result.transaction:\n            transaction_id = result.transaction.id\n\n            # Verify that Braintree is sending us the right payload.\n            # TODO This is hard to test and it should be a pretty tight guarantee,\n            # so I am commenting out for now. :(\n            #pfos_uuid = result.transaction.custom_fields['pfos_uuid']\n            #assert pfos_uuid == self.uuid, (pfos_uuid, transaction_id)\n\n        self.db.run(\"\"\"\n            UPDATE payments_for_open_source\n               SET braintree_result_message=%s\n                 , braintree_transaction_id=%s\n             WHERE uuid=%s\n        \"\"\", (result_message, transaction_id, self.uuid))\n        self.set_attributes( braintree_result_message=result_message\n                           , braintree_transaction_id=transaction_id\n                            )\n", "path": "gratipay/models/payment_for_open_source.py"}, {"content": "# -*- coding: utf-8 -*-\n\"\"\"This is the Python library behind gratipay.com.\n\"\"\"\nfrom __future__ import absolute_import, division, print_function, unicode_literals\n\nfrom gratipay import utils\nfrom gratipay.models.payment_for_open_source import PaymentForOpenSource\n\n\nkB = 2**10\n\n\ndef _parse(raw):\n    \"\"\"Given a POST request.body, return (parsed<dict>, errors<list>).\n    \"\"\"\n\n    errors = []\n    x = lambda f: raw[f].strip()  # KeyError -> 400\n\n    # payment info\n    amount = x('amount')\n    if (not amount.isdigit()) or (int(amount) < 10):\n        errors.append('amount')\n        amount = ''.join(x for x in amount.split('.')[0] if x.isdigit())\n\n    payment_method_nonce = x('payment_method_nonce')\n    if len(payment_method_nonce) > 36:\n        errors.append('payment_method_nonce')\n        payment_method_nonce = ''\n\n    # ecosystems/packages\n    grateful_for = x('grateful_for')\n    if len(grateful_for) > 16*kB:\n        grateful_for = grateful_for[:16*kB]\n        errors.append('grateful_for')\n\n    # contact info\n    name = x('name')\n    if len(name) > 255:\n        name = name[:255]\n        errors.append('name')\n\n    email_address = x('email_address')\n    if email_address and not utils.is_valid_email_address(email_address):\n        email_address = email_address[:255]\n        errors.append('email_address')\n\n    follow_up = x('follow_up')\n    if follow_up not in ('quarterly', 'yearly', 'never'):\n        follow_up = 'quarterly'\n        errors.append('follow_up')\n\n    # promo fields\n    promotion_name = x('promotion_name')\n    if len(promotion_name) > 32:\n        promotion_name = promotion_name[:32]\n        errors.append('promotion_name')\n\n    promotion_url = x('promotion_url')\n    is_link = lambda x: (x.startswith('http://') or x.startswith('https://')) and '.' in x\n    if len(promotion_url) > 255 or (promotion_url and not is_link(promotion_url)):\n        promotion_url = promotion_url[:255]\n        errors.append('promotion_url')\n\n    promotion_twitter = x('promotion_twitter')\n    if len(promotion_twitter) > 32:\n        promotion_twitter = promotion_twitter[:32]\n        # TODO What are Twitter's rules?\n        errors.append('promotion_twitter')\n\n    promotion_message = x('promotion_message')\n    if len(promotion_message) > 128:\n        promotion_message = promotion_message[:128]\n        errors.append('promotion_message')\n\n    parsed = { 'amount': amount\n             , 'payment_method_nonce': payment_method_nonce\n             , 'grateful_for': grateful_for\n             , 'name': name\n             , 'email_address': email_address\n             , 'follow_up': follow_up\n             , 'promotion_name': promotion_name\n             , 'promotion_url': promotion_url\n             , 'promotion_twitter': promotion_twitter\n             , 'promotion_message': promotion_message\n              }\n    return parsed, errors\n\n\ndef _store(parsed):\n    return PaymentForOpenSource.insert(**parsed)\n\n\ndef _charge(app, pfos, nonce):\n    params = { 'amount': pfos.amount\n             , 'payment_method_nonce': nonce\n             , 'options': {'submit_for_settlement': True}\n             , 'custom_fields': {'pfos_uuid': pfos.uuid}\n              }\n    result = app.pfos_card_charger.charge(params)\n    pfos.process_result(result)\n\n\ndef _send(app, pfos):\n    app.email_queue.put( to=None\n                       , template='paid-for-open-source'\n                       , email=pfos.email_address\n                       , amount=pfos.amount\n                       , invoice_url=pfos.invoice_url\n                        )\n\n\ndef pay_for_open_source(app, raw):\n    parsed, errors = _parse(raw)\n    out = {'errors': errors, 'invoice_url': None}\n    if not errors:\n        payment_method_nonce = parsed.pop('payment_method_nonce')\n        pfos = _store(parsed)\n        _charge(app, pfos, payment_method_nonce)\n        if pfos.succeeded:\n            out['invoice_url'] = pfos.invoice_url\n            if pfos.email_address:\n                _send(app, pfos)\n        else:\n            out['errors'].append('charging')\n    return out\n", "path": "gratipay/homepage.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\nfrom __future__ import absolute_import, division, print_function, unicode_literals\n\nimport gratipay\nfrom uuid import uuid4\nfrom postgres.orm import Model\n\n\nclass PaymentForOpenSource(Model):\n\n    typname = \"payments_for_open_source\"\n\n    def __repr__(self):\n        return '<PaymentForOpenSource: {}>'.format(repr(self.amount))\n\n\n    @property\n    def succeeded(self):\n        return self.braintree_result_message == ''\n\n\n    @property\n    def invoice_url(self):\n        if not self.succeeded:\n            return None\n        return '{}/browse/payments/{}/invoice.html'.format(gratipay.base_url, self.uuid)\n\n\n    @classmethod\n    def from_uuid(cls, uuid, cursor=None):\n        \"\"\"Take a uuid and return an object.\n        \"\"\"\n        return (cursor or cls.db).one(\"\"\"\n            SELECT pfos.*::payments_for_open_source\n              FROM payments_for_open_source pfos\n             WHERE uuid = %s\n        \"\"\", (uuid,))\n\n\n    @classmethod\n    def insert(cls, amount, grateful_for, name, on_mailing_list, email_address,\n               promotion_name, promotion_url, promotion_twitter, promotion_message,\n               cursor=None):\n        \"\"\"Take baseline info and insert into the database.\n        \"\"\"\n        uuid = uuid4().hex\n        on_mailing_list = on_mailing_list == 'yes'\n        return (cursor or cls.db).one(\"\"\"\n            INSERT INTO payments_for_open_source\n                        (uuid, amount, grateful_for, name, on_mailing_list, email_address,\n                         promotion_name, promotion_url, promotion_twitter, promotion_message)\n                 VALUES (%s, %s, %s, %s, %s, %s,\n                         %s, %s, %s, %s)\n              RETURNING payments_for_open_source.*::payments_for_open_source\n        \"\"\", (uuid, amount, grateful_for, name, on_mailing_list, email_address,\n              promotion_name, promotion_url, promotion_twitter, promotion_message))\n\n\n    def process_result(self, result):\n        \"\"\"Take a Braintree API result and update the database.\n        \"\"\"\n        result_message = '' if result.is_success else result.message\n        transaction_id = None\n        if result.transaction:\n            transaction_id = result.transaction.id\n\n            # Verify that Braintree is sending us the right payload.\n            # TODO This is hard to test and it should be a pretty tight guarantee,\n            # so I am commenting out for now. :(\n            #pfos_uuid = result.transaction.custom_fields['pfos_uuid']\n            #assert pfos_uuid == self.uuid, (pfos_uuid, transaction_id)\n\n        self.db.run(\"\"\"\n            UPDATE payments_for_open_source\n               SET braintree_result_message=%s\n                 , braintree_transaction_id=%s\n             WHERE uuid=%s\n        \"\"\", (result_message, transaction_id, self.uuid))\n        self.set_attributes( braintree_result_message=result_message\n                           , braintree_transaction_id=transaction_id\n                            )\n", "path": "gratipay/models/payment_for_open_source.py"}, {"content": "# -*- coding: utf-8 -*-\n\"\"\"This is the Python library behind gratipay.com.\n\"\"\"\nfrom __future__ import absolute_import, division, print_function, unicode_literals\n\nfrom gratipay import utils\nfrom gratipay.models.payment_for_open_source import PaymentForOpenSource\n\n\nkB = 2**10\n\n\ndef _parse(raw):\n    \"\"\"Given a POST request.body, return (parsed<dict>, errors<list>).\n    \"\"\"\n\n    errors = []\n    x = lambda f: raw[f].strip()  # KeyError -> 400\n\n    # payment info\n    amount = x('amount')\n    if (not amount.isdigit()) or (int(amount) < 10):\n        errors.append('amount')\n        amount = ''.join(x for x in amount.split('.')[0] if x.isdigit())\n\n    payment_method_nonce = x('payment_method_nonce')\n    if len(payment_method_nonce) > 36:\n        errors.append('payment_method_nonce')\n        payment_method_nonce = ''\n\n    # ecosystems/packages\n    grateful_for = x('grateful_for')\n    if len(grateful_for) > 16*kB:\n        grateful_for = grateful_for[:16*kB]\n        errors.append('grateful_for')\n\n    # contact info\n    name = x('name')\n    if len(name) > 255:\n        name = name[:255]\n        errors.append('name')\n\n    email_address = x('email_address')\n    if email_address and not utils.is_valid_email_address(email_address):\n        email_address = email_address[:255]\n        errors.append('email_address')\n\n    on_mailing_list = x('on_mailing_list')\n    if on_mailing_list not in ('yes', 'no'):\n        on_mailing_list = 'yes'\n        errors.append('on_mailing_list')\n\n    # promo fields\n    promotion_name = x('promotion_name')\n    if len(promotion_name) > 32:\n        promotion_name = promotion_name[:32]\n        errors.append('promotion_name')\n\n    promotion_url = x('promotion_url')\n    is_link = lambda x: (x.startswith('http://') or x.startswith('https://')) and '.' in x\n    if len(promotion_url) > 255 or (promotion_url and not is_link(promotion_url)):\n        promotion_url = promotion_url[:255]\n        errors.append('promotion_url')\n\n    promotion_twitter = x('promotion_twitter')\n    if len(promotion_twitter) > 32:\n        promotion_twitter = promotion_twitter[:32]\n        # TODO What are Twitter's rules?\n        errors.append('promotion_twitter')\n\n    promotion_message = x('promotion_message')\n    if len(promotion_message) > 128:\n        promotion_message = promotion_message[:128]\n        errors.append('promotion_message')\n\n    parsed = { 'amount': amount\n             , 'payment_method_nonce': payment_method_nonce\n             , 'grateful_for': grateful_for\n             , 'name': name\n             , 'email_address': email_address\n             , 'on_mailing_list': on_mailing_list\n             , 'promotion_name': promotion_name\n             , 'promotion_url': promotion_url\n             , 'promotion_twitter': promotion_twitter\n             , 'promotion_message': promotion_message\n              }\n    return parsed, errors\n\n\ndef _store(parsed):\n    return PaymentForOpenSource.insert(**parsed)\n\n\ndef _charge(app, pfos, nonce):\n    params = { 'amount': pfos.amount\n             , 'payment_method_nonce': nonce\n             , 'options': {'submit_for_settlement': True}\n             , 'custom_fields': {'pfos_uuid': pfos.uuid}\n              }\n    result = app.pfos_card_charger.charge(params)\n    pfos.process_result(result)\n\n\ndef _send(app, pfos):\n    app.email_queue.put( to=None\n                       , template='paid-for-open-source'\n                       , email=pfos.email_address\n                       , amount=pfos.amount\n                       , invoice_url=pfos.invoice_url\n                        )\n\n\ndef pay_for_open_source(app, raw):\n    parsed, errors = _parse(raw)\n    out = {'errors': errors, 'invoice_url': None}\n    if not errors:\n        payment_method_nonce = parsed.pop('payment_method_nonce')\n        pfos = _store(parsed)\n        _charge(app, pfos, payment_method_nonce)\n        if pfos.succeeded:\n            out['invoice_url'] = pfos.invoice_url\n            if pfos.email_address:\n                _send(app, pfos)\n        else:\n            out['errors'].append('charging')\n    return out\n", "path": "gratipay/homepage.py"}]}