From 72dde08c690108a758ef7cb8ea24f8ed9d55aac2 Mon Sep 17 00:00:00 2001 From: Chad Whitacre Date: Fri, 16 May 2014 01:42:44 -0400 Subject: [PATCH 01/23] Rough in UI for account cancelation; #54 Now that the three scripts involved in account cancelation have been moved to (tested) methods on Participant, we're ready to wire up the UI. This commit rebases the original work we did on the UI on top of the separate line of development we ended up pursuing for the Participant methods. --- bin/deactivate.py | 84 ---------------------------- bin/final-gift.py | 41 -------------- bin/untip.py | 19 ------- js/gittip/account.js | 10 ++++ js/gittip/cancel.js | 4 ++ scss/buttons-knobs.scss | 3 +- scss/layout.scss | 12 +++- www/%username/account/cancel.spt | 57 +++++++++++++++++++ www/%username/account/index.html.spt | 11 ++++ 9 files changed, 95 insertions(+), 146 deletions(-) delete mode 100644 bin/deactivate.py delete mode 100644 bin/final-gift.py delete mode 100644 bin/untip.py create mode 100644 js/gittip/cancel.js create mode 100644 www/%username/account/cancel.spt diff --git a/bin/deactivate.py b/bin/deactivate.py deleted file mode 100644 index fb1e5aea12..0000000000 --- a/bin/deactivate.py +++ /dev/null @@ -1,84 +0,0 @@ -#!/usr/bin/env python -"""The final rename and clear step for canceling an account. - -If the account has a balance or is involved in active tips, -this script will report the problem and abort without making any update. - -If the first eight digits of the account's API key are not given or do not match, -this script will report the problem and abort without making any update. - -Usage: - - [gittip] $ heroku config -s -a gittip | foreman run -e /dev/stdin ./env/bin/python ./bin/deactivate.py "username" [first-eight-of-api-key] - -""" -from __future__ import print_function - -import sys - -from gittip import wireup -from gittip.models.participant import Participant - - -username = sys.argv[1] # will fail with KeyError if missing -if len(sys.argv) < 3: - first_eight = "unknown!" -else: - first_eight = sys.argv[2] - -db = wireup.db(wireup.env()) - - -# Ensure that balance and tips have been dealt with. -# ================================================== - -target = Participant.from_username(username) - -INCOMING = """ - SELECT count(*) - FROM current_tips - WHERE tippee = %s - AND amount > 0 -""" - -FIELDS = """ - SELECT username, username_lower, api_key, claimed_time - FROM participants - WHERE username = %s -""" - -incoming = db.one(INCOMING, (username,)) -fields = db.one(FIELDS, (username,)) - -print("Current balance ", target.balance) -print("Incoming tip count ", incoming) -print(fields) - -assert target.balance == 0 -assert incoming == 0 -if fields.api_key == None: - assert first_eight == "None" -else: - assert fields.api_key[0:8] == first_eight - - -# Archive the participant record. -# =============================== - -deactivated_name = "deactivated-" + username -print("Renaming " + username + " to " + deactivated_name) - -RENAME = """ - UPDATE participants - SET claimed_time = null - , session_token = null - , username = %s - , username_lower = %s - WHERE username = %s -""" - -print(RENAME % (deactivated_name, deactivated_name.lower(), username)) - -db.run(RENAME, (deactivated_name, deactivated_name.lower(), username)) - -print("All done.") diff --git a/bin/final-gift.py b/bin/final-gift.py deleted file mode 100644 index c73af66397..0000000000 --- a/bin/final-gift.py +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env python -"""Distribute a balance as a final gift. This addresses part of #54. - -Usage: - - [gittip] $ heroku config -s -a gittip | foreman run -e /dev/stdin ./env/bin/python ./bin/final-gift.py "username" [first-eight-of-api-key] - -""" -from __future__ import print_function - -import sys - -from gittip import wireup -from gittip.models.participant import Participant - -db = wireup.db(wireup.env()) - -username = sys.argv[1] # will fail with KeyError if missing -tipper = Participant.from_username(username) -if len(sys.argv) < 3: - first_eight = "unknown!" -else: - first_eight = sys.argv[2] - -# Ensure user is legit -FIELDS = """ - SELECT username, username_lower, api_key, claimed_time - FROM participants - WHERE username = %s -""" - -fields = db.one(FIELDS, (username,)) -print(fields) - -if fields.api_key == None: - assert first_eight == "None" -else: - assert fields.api_key[0:8] == first_eight - -print("Distributing {} from {}.".format(tipper.balance, tipper.username)) -tipper.distribute_balance_as_final_gift() diff --git a/bin/untip.py b/bin/untip.py deleted file mode 100644 index 17bf953043..0000000000 --- a/bin/untip.py +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env python -"""Zero out tips to a given user. This is a workaround for #1469. - -Usage: - - [gittip] $ heroku config -s -a gittip | foreman run -e /dev/stdin ./env/bin/python ./scripts/untip.py "username" - -""" -from __future__ import print_function - -import sys - -from gittip import wireup -from gittip.models.participant import Participant - - -tippee = sys.argv[1] # will fail with KeyError if missing -db = wireup.db(wireup.env()) -Participant.from_username(tippee).clear_tips_receiving() diff --git a/js/gittip/account.js b/js/gittip/account.js index 52da2336a8..2fffa5572d 100644 --- a/js/gittip/account.js +++ b/js/gittip/account.js @@ -163,4 +163,14 @@ Gittip.account.init = function() { return false; }); + + // Wire up cancel knob. + // ==================== + + $('.cancel form').submit(function(e) { + e.stopPropagation(); + e.preventDefault(); + if (confirm("Really cancel account!? This can't be undone!")) + window.location.href = './cancel'; + }); }; diff --git a/js/gittip/cancel.js b/js/gittip/cancel.js new file mode 100644 index 0000000000..52c14c619a --- /dev/null +++ b/js/gittip/cancel.js @@ -0,0 +1,4 @@ +Gittip.cancel = {}; + +Gittip.cancel.init = function() { +} diff --git a/scss/buttons-knobs.scss b/scss/buttons-knobs.scss index d7a9a8283c..4b3fe7262d 100644 --- a/scss/buttons-knobs.scss +++ b/scss/buttons-knobs.scss @@ -48,7 +48,8 @@ button.selected:hover, button.selected.drag, } button.join-leave[data-is-member="true"], -.button.join-leave[data-is-member="true"] { +.button.join-leave[data-is-member="true"], +button.cancel-account { font-weight: normal; background: none; color: $red; diff --git a/scss/layout.scss b/scss/layout.scss index c76e9571c5..d6f3450df4 100644 --- a/scss/layout.scss +++ b/scss/layout.scss @@ -420,7 +420,14 @@ padding: 0; text-transform: uppercase; } - input { + input[type="radio"] + label { + display: inline; + margin: 0; + } + input[type="radio"]:disabled + label { + color: #888; + } + input:not([type]), input[type="text"] { font: normal 11pt/14pt $Helvetica; width: 292px; margin: 0; @@ -431,6 +438,9 @@ input.disabled { color: $light-brown; } + input[type="radio"] { + vertical-align: middle; + } .half input { width: 137px; diff --git a/www/%username/account/cancel.spt b/www/%username/account/cancel.spt new file mode 100644 index 0000000000..53fd49a307 --- /dev/null +++ b/www/%username/account/cancel.spt @@ -0,0 +1,57 @@ +from gittip.utils import get_participant +[---] +participant = get_participant(request, restrict=True) +hero = "Cancel Account" +title = participant.username # used in the title tag +username = participant.username # used in footer shared with on/$platform/ + # pages + +can_upstream = participant.get_dollars_receiving() > 0 +can_downstream = participant.get_dollars_giving() > 0 + +if POST: + final_disbursement_option = request.body['final_disbursement_option'] + participant.cancel(final_disbursement_option) + request.redirect('/') +[---] text/html +{% extends "templates/base.html" %} + +{% block heading %} +

Cancel Account

+{% endblock %} + +{% block box %} +
+
+
+ {% if participant.balance > 0 %} +

You have a balance of ${{ participant.balance }}. + What should we do with it?

+ +
    +
  • +
  • +
  • +
  • +
  • +
  • +
+ {% endif %} +

Here's what we'll do when you cancel your account:

+
    +
  • Turn off all tips you are currently receiving.
  • +
  • Zero out all tips you are currently receiving.
  • +
  • Make it so there are no more you are currently receiving.
  • +
+
+ +
+
+{% endblock %} diff --git a/www/%username/account/index.html.spt b/www/%username/account/index.html.spt index 58a470e428..4d0bebce0b 100644 --- a/www/%username/account/index.html.spt +++ b/www/%username/account/index.html.spt @@ -184,6 +184,17 @@ locked = False + + +
+

Cancel

+
+
+ +
+
+
+ {% endblock %} From bc126daba09ded9aced08cfc9ab9ab3d8b02306b Mon Sep 17 00:00:00 2001 From: Chad Whitacre Date: Thu, 5 Jun 2014 23:06:59 -0400 Subject: [PATCH 02/23] Rough in UI for case where there is no balance --- gittip/models/participant.py | 1 - www/%username/account/cancel.spt | 35 ++++++++++++++++---------------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/gittip/models/participant.py b/gittip/models/participant.py index 4323b96a02..4894f034d3 100644 --- a/gittip/models/participant.py +++ b/gittip/models/participant.py @@ -258,7 +258,6 @@ def cancel(self, disbursement_strategy): """Cancel the participant's account. """ with self.db.get_cursor() as cursor: - if disbursement_strategy == None: pass # No balance, supposedly. archive will check. elif disbursement_strategy == 'bank': diff --git a/www/%username/account/cancel.spt b/www/%username/account/cancel.spt index 53fd49a307..64516477d0 100644 --- a/www/%username/account/cancel.spt +++ b/www/%username/account/cancel.spt @@ -10,8 +10,8 @@ can_upstream = participant.get_dollars_receiving() > 0 can_downstream = participant.get_dollars_giving() > 0 if POST: - final_disbursement_option = request.body['final_disbursement_option'] - participant.cancel(final_disbursement_option) + disbursement_strategy = request.body.get('disbursement_strategy') + participant.cancel(disbursement_strategy) request.redirect('/') [---] text/html {% extends "templates/base.html" %} @@ -25,32 +25,33 @@ if POST:
{% if participant.balance > 0 %} -

You have a balance of ${{ participant.balance }}. +

Balance

+

You have a balance of ${{ participant.balance }}.
What should we do with it?

    -
  • -
  • -
  • -
  • -
  • Send it to my bank + account
  • +
  • -
  • +
+ +

If neither option works, please contact + support.

+ {% endif %} -

Here's what we'll do when you cancel your account:

-
    -
  • Turn off all tips you are currently receiving.
  • -
  • Zero out all tips you are currently receiving.
  • -
  • Make it so there are no more you are currently receiving.
  • -
+ +

Really cancel your account?

From 9341ae0c8b4f111becfee09e0c620c8e5292a913 Mon Sep 17 00:00:00 2001 From: Chad Whitacre Date: Fri, 6 Jun 2014 09:45:23 -0400 Subject: [PATCH 03/23] Fill out verbiage on cancel page Based on conversation in IRC: https://botbot.me/freenode/gittip/2014-06-06/?msg=15781294&page=1 --- www/%username/account/cancel.spt | 49 ++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/www/%username/account/cancel.spt b/www/%username/account/cancel.spt index 64516477d0..70c031045f 100644 --- a/www/%username/account/cancel.spt +++ b/www/%username/account/cancel.spt @@ -24,6 +24,7 @@ if POST:
+ {% if participant.balance > 0 %}

Balance

You have a balance of ${{ participant.balance }}.
@@ -40,13 +41,55 @@ if POST: tip -

If neither option works, please If neither option works for you, please contact - support.

+ support to otherwise deal with your balance before + canceling your account.

{% endif %} -

Really cancel your account?

+ +

Username

+ +

We may give your username to someone else if they ask for + it, but not for at least a year after you cancel.

+ +

Though if we determine that you've been infringing a + trademark, we may give your username away sooner.

+ +

Until we give your username to someone else, you can use it + again if you ever decide to rejoin Gittip. Simply sign in.

+ + +

Personal Information

+ +

Most of your personal information will be deleted + immediately, including your "making the world better" + statement, the tips you're receiving, and the tips you're + giving. You'll also be removed from any communities and teams + you were a part of. If you're canceling a team account, then + all team members will be removed from the team.

+ +

We won't delete your past giving and receiving + history on the site, because that information also belongs + equally to other users (the ones you gave to and received + from).

+ +

After you cancel, your profile page will say, "Account + canceled."

+ + +

Remember

+ +

We have no control over links to your profile from other + places on the Internet.

+ +

We have no control over content indexed by search engines + like Google.

+ + +

Ready?

+
diff --git a/www/%username/account/index.html.spt b/www/%username/account/index.html.spt index 4d0bebce0b..97f91aa90f 100644 --- a/www/%username/account/index.html.spt +++ b/www/%username/account/index.html.spt @@ -185,12 +185,12 @@ locked = False - -
-

Cancel

-
+ +
+

Close

+
- +
From 14a64be7e830b94cdd4ec50b532865fa3f642bfd Mon Sep 17 00:00:00 2001 From: Chad Whitacre Date: Fri, 6 Jun 2014 12:00:30 -0400 Subject: [PATCH 06/23] Add border to red button to make it a button Looked like a link before. --- scss/buttons-knobs.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scss/buttons-knobs.scss b/scss/buttons-knobs.scss index 4b3fe7262d..18e4e78c4c 100644 --- a/scss/buttons-knobs.scss +++ b/scss/buttons-knobs.scss @@ -49,11 +49,11 @@ button.selected:hover, button.selected.drag, button.join-leave[data-is-member="true"], .button.join-leave[data-is-member="true"], -button.cancel-account { +button.close-account { font-weight: normal; background: none; color: $red; - text-decoration: underline; + border: 1px solid $red; &:hover { background: $red; color: white; From 1df3b54173bd11d3a766a4db88bbe276a56b5218 Mon Sep 17 00:00:00 2001 From: Chad Whitacre Date: Fri, 6 Jun 2014 12:06:25 -0400 Subject: [PATCH 07/23] Drop js confirm for account close We have a whole confirmation page, we don't need the js confirm. --- js/gittip/account.js | 7 ++----- www/%username/account/index.html.spt | 8 +++----- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/js/gittip/account.js b/js/gittip/account.js index b666a23e33..4ae3c6958f 100644 --- a/js/gittip/account.js +++ b/js/gittip/account.js @@ -167,10 +167,7 @@ Gittip.account.init = function() { // Wire up close knob. // =================== - $('.close form').submit(function(e) { - e.stopPropagation(); - e.preventDefault(); - if (confirm("Really close account!? This can't be undone!")) - window.location.href = './close'; + $('button.close-account').click(function() { + window.location.href = './close'; }); }; diff --git a/www/%username/account/index.html.spt b/www/%username/account/index.html.spt index 97f91aa90f..221ee01fc5 100644 --- a/www/%username/account/index.html.spt +++ b/www/%username/account/index.html.spt @@ -188,11 +188,9 @@ locked = False

Close

-
-
- -
-
+
+ +
From e2e67e7839de9b5d5757baf362d0a2f6350e92ee Mon Sep 17 00:00:00 2001 From: Chad Whitacre Date: Fri, 6 Jun 2014 12:11:48 -0400 Subject: [PATCH 08/23] Clarify wording around what's deleted on close --- www/%username/account/close.spt | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/www/%username/account/close.spt b/www/%username/account/close.spt index 34ca1ca38d..b7658890ee 100644 --- a/www/%username/account/close.spt +++ b/www/%username/account/close.spt @@ -63,19 +63,19 @@ if POST:

Personal Information

-

Most of your personal information will be deleted +

Almost all of your personal information will be deleted immediately, including your "making the world better" - statement, the tips you're receiving, the tips you're giving, - any bank account or credit card, and any linked accounts that - aren't used for sign-in. You'll also be removed from any - communities and teams you were a part of. If you're closing a - team account, then all team members will be removed from the - team.

- -

We won't delete your past giving and receiving - history on the site, because that information also belongs - equally to other users (the ones you gave to and received - from).

+ statement, any funding goal, the tips you're receiving, the + tips you're giving, any bank account or credit card, and any + linked accounts that aren't used for sign-in. You'll also be + removed from any communities and teams you were a part of. If + you're closing a team account, then all team members will be + removed from the team.

+ +

What we don't delete is your past giving and + receiving history on the site, because that information also + belongs equally to other users (the ones you gave to and + received from).

After you close your account, your profile page will say, "Account closed by user."

From 826f9ae66d6344b45605cd7960581bd4617cf925 Mon Sep 17 00:00:00 2001 From: Chad Whitacre Date: Fri, 6 Jun 2014 12:33:53 -0400 Subject: [PATCH 09/23] Here's the test fixture for cancel -> close --- .../{TestCanceling.yml => TestClosing.yml} | 174 +++++++++--------- 1 file changed, 87 insertions(+), 87 deletions(-) rename tests/py/fixtures/{TestCanceling.yml => TestClosing.yml} (63%) diff --git a/tests/py/fixtures/TestCanceling.yml b/tests/py/fixtures/TestClosing.yml similarity index 63% rename from tests/py/fixtures/TestCanceling.yml rename to tests/py/fixtures/TestClosing.yml index 2ebb302598..4ef3423739 100644 --- a/tests/py/fixtures/TestCanceling.yml +++ b/tests/py/fixtures/TestClosing.yml @@ -15,10 +15,10 @@ response: body: {string: "{\n \"customers\": [\n {\n \"name\": null,\n \"\ links\": {\n \"source\": null,\n \"destination\": null\n \ - \ },\n \"updated_at\": \"2014-06-06T02:27:17.902166Z\",\n \"created_at\"\ - : \"2014-06-06T02:27:17.770697Z\",\n \"dob_month\": null,\n \"id\"\ - : \"CUCrb24TQtaUrFR69oRDmLP\",\n \"phone\": null,\n \"href\": \"\ - /customers/CUCrb24TQtaUrFR69oRDmLP\",\n \"merchant_status\": \"no-match\"\ + \ },\n \"updated_at\": \"2014-06-06T16:33:38.386693Z\",\n \"created_at\"\ + : \"2014-06-06T16:33:38.274538Z\",\n \"dob_month\": null,\n \"id\"\ + : \"CU2qN2ov0SNIumvBA8iWOm81\",\n \"phone\": null,\n \"href\": \"\ + /customers/CU2qN2ov0SNIumvBA8iWOm81\",\n \"merchant_status\": \"no-match\"\ ,\n \"meta\": {},\n \"dob_year\": null,\n \"address\": {\n\ \ \"city\": null,\n \"line2\": null,\n \"line1\": null,\n\ \ \"state\": null,\n \"postal_code\": null,\n \"country_code\"\ @@ -36,19 +36,19 @@ \ \"customers.reversals\": \"/customers/{customers.id}/reversals\",\n \"\ customers.orders\": \"/customers/{customers.id}/orders\",\n \"customers.credits\"\ : \"/customers/{customers.id}/credits\"\n }\n}"} - headers: ["Content-Type: application/json\r\n", "Date: Fri, 06 Jun 2014 02:27:17\ - \ GMT\r\n", "Server: ngx_openresty/1.2.6.3\r\n", "X-Balanced-Guru: OHM1459e22eed2211e3ab2502a1fe52a36c\r\ - \n", "x-balanced-host: bapi-integration-prod-8u30f7-10-3-4-8\r\n", "x-balanced-marketplace:\ + headers: ["Content-Type: application/json\r\n", "Date: Fri, 06 Jun 2014 16:33:38\ + \ GMT\r\n", "Server: ngx_openresty/1.2.6.3\r\n", "X-Balanced-Guru: OHM4fe477e8ed9811e39cde02a1fe53e539\r\ + \n", "x-balanced-host: bapi-integration-prod-8u30f7-10-3-5-201\r\n", "x-balanced-marketplace:\ \ TEST-MP1wTyxDAJKCaWPjBS4Oli4d\r\n", "x-balanced-merchant: TEST-MR1wiJbFKXFmlmVIyaPiduUV\r\ \n", "x-balanced-revision: 1.1\r\n", "x-balanced-software-build: 1.2.50\r\n", - "X-Midlr-Version: 2\r\n", "x-newrelic-app-data: PxQFWFNXCQYTVVhWAwQDVUYdFhE1AwE2QgNWEVlbQFtcCxYxSBVbDQoZVA4IF0pcXAgEEBhSQhUQXltWEBVNAUIHWRZdGhgSA0kZUR9TAFdQBA9RX1cABQNcWFMBHRdUSBEUUFsCBltTUlxVDg0MBVFVC0MdQVUDCEVSPA==\r\ - \n", "Content-Length: 1591\r\n", "Connection: keep-alive\r\n"] + "X-Midlr-Version: 2\r\n", "x-newrelic-app-data: PxQFWFNXCQYTVVhWAwQDVUYdFhE1AwE2QgNWEVlbQFtcCxYxSBVbDQoZVA4IF0pcXAgEEBhSQhUQXltWEBVNAUIHWRZdGhgSA0kZUR9QC1ZcDgJRVFYIAQRQUFYHBxtGVh0WEQFSAFZWW10DDw0CUFVSW1ETTRMEBVpEBDs=\r\ + \n", "Content-Length: 1593\r\n", "Connection: keep-alive\r\n"] status: {code: 201, message: CREATED} - request: !!python/object:vcr.request.Request body: '{"routing_number": "321174851", "account_type": "checking", "account_number": - "9900000001", "name": "alice"}' + "9900000001", "name": "Alice G. Krebs"}' headers: !!python/object/apply:__builtin__.frozenset - - - !!python/tuple [Content-Length, !!python/unicode '108'] + - - !!python/tuple [Content-Length, !!python/unicode '117'] - !!python/tuple [User-Agent, !!python/unicode 'balanced-python/1.0.1beta2'] - !!python/tuple [Authorization, !!python/unicode 'Basic YWstdGVzdC1Xbnh5QTA2N1pYd0JsYW5xbWdOZm90bUFPeUpsRm5rZTpOb25l'] - !!python/tuple [Content-Type, !!python/unicode 'application/json'] @@ -62,16 +62,16 @@ response: body: {string: "{\n \"bank_accounts\": [\n {\n \"routing_number\": \"\ 321174851\",\n \"bank_name\": \"SAN MATEO CREDIT UNION\",\n \"account_type\"\ - : \"checking\",\n \"name\": \"alice\",\n \"links\": {\n \"\ - customer\": null,\n \"bank_account_verification\": null\n },\n\ - \ \"can_credit\": true,\n \"created_at\": \"2014-06-06T02:27:18.955306Z\"\ + : \"checking\",\n \"name\": \"Alice G. Krebs\",\n \"links\": {\n\ + \ \"customer\": null,\n \"bank_account_verification\": null\n\ + \ },\n \"can_credit\": true,\n \"created_at\": \"2014-06-06T16:33:39.496116Z\"\ ,\n \"fingerprint\": \"5f0ba9fa3f1122ef13b944a40abfe44e7eba9e16934e64200913cb4c402ace14\"\ - ,\n \"updated_at\": \"2014-06-06T02:27:18.955308Z\",\n \"href\"\ - : \"/bank_accounts/BADLD5GZ8Uv8SXloInEFuKB\",\n \"meta\": {},\n \ - \ \"account_number\": \"xxxxxx0001\",\n \"address\": {\n \"city\"\ + ,\n \"updated_at\": \"2014-06-06T16:33:39.496118Z\",\n \"href\"\ + : \"/bank_accounts/BA2s9HPLLimkYjd9Xufqeb3f\",\n \"meta\": {},\n \ + \ \"account_number\": \"xxxxxx0001\",\n \"address\": {\n \"city\"\ : null,\n \"line2\": null,\n \"line1\": null,\n \"state\"\ : null,\n \"postal_code\": null,\n \"country_code\": null\n\ - \ },\n \"can_debit\": false,\n \"id\": \"BADLD5GZ8Uv8SXloInEFuKB\"\ + \ },\n \"can_debit\": false,\n \"id\": \"BA2s9HPLLimkYjd9Xufqeb3f\"\ \n }\n ],\n \"links\": {\n \"bank_accounts.credits\": \"/bank_accounts/{bank_accounts.id}/credits\"\ ,\n \"bank_accounts.bank_account_verifications\": \"/bank_accounts/{bank_accounts.id}/verifications\"\ ,\n \"bank_accounts.customer\": \"/customers/{bank_accounts.customer}\"\ @@ -80,53 +80,53 @@ \n }\n}"} headers: ["access-control-allow-headers: Content-Type\r\n", "access-control-allow-methods:\ \ POST, OPTIONS\r\n", "access-control-allow-origin: *\r\n", "Content-Type:\ - \ application/json\r\n", "Date: Fri, 06 Jun 2014 02:27:19 GMT\r\n", "Server:\ - \ ngx_openresty/1.2.6.3\r\n", "X-Balanced-Guru: OHM14d5b37ced2211e3a52c02a1fe53e539\r\ - \n", "x-balanced-host: bapi-integration-prod-8u30f7-10-3-5-201\r\n", "x-balanced-marketplace:\ + \ application/json\r\n", "Date: Fri, 06 Jun 2014 16:33:39 GMT\r\n", "Server:\ + \ ngx_openresty/1.2.6.3\r\n", "X-Balanced-Guru: OHM506b90b6ed9811e3a82802a1fe52a36c\r\ + \n", "x-balanced-host: bapi-integration-prod-8u30f7-10-3-4-8\r\n", "x-balanced-marketplace:\ \ TEST-MP1wTyxDAJKCaWPjBS4Oli4d\r\n", "x-balanced-merchant: TEST-MR1wiJbFKXFmlmVIyaPiduUV\r\ \n", "x-balanced-revision: 1.1\r\n", "x-balanced-software-build: 1.2.50\r\n", - "X-Midlr-Version: 2\r\n", "x-newrelic-app-data: PxQFWFNXCQYTVVhWAwQDVUYdFhE1AwE2QgNWEVlbQFtcCxYxSBVbDQoZVA4IF0pcXAgEEBhTVggPbldQAQkWDEQRFgFKXVVGVkcVQQFNE1JKBQJWUlcAAgBVUVYEBgdXVQYaE1BXVk4QQFtWDwAHAQQECQQJAFEBUwAVTUYFWV9DATw=\r\ - \n", "Content-Length: 1281\r\n", "Connection: keep-alive\r\n"] + "X-Midlr-Version: 2\r\n", "x-newrelic-app-data: PxQFWFNXCQYTVVhWAwQDVUYdFhE1AwE2QgNWEVlbQFtcCxYxSBVbDQoZVA4IF0pcXAgEEBhTVggPbldQAQkWDEQRFgFKXVVGVkcVQQFNE1JKAgVZVFQPCgZdUlQHAgJXUQcaE1BUV04QQF0BXQgGBlUACFgIWQNUUQMVTUYFWV9DATw=\r\ + \n", "Content-Length: 1292\r\n", "Connection: keep-alive\r\n"] status: {code: 201, message: CREATED} - request: !!python/object:vcr.request.Request body: '{"routing_number": "321174851", "bank_name": "SAN MATEO CREDIT UNION", - "account_type": "checking", "name": "alice", "links": {"customer": "/customers/CUCrb24TQtaUrFR69oRDmLP"}, - "can_credit": true, "created_at": "2014-06-06T02:27:18.955306Z", "fingerprint": - "5f0ba9fa3f1122ef13b944a40abfe44e7eba9e16934e64200913cb4c402ace14", "updated_at": - "2014-06-06T02:27:18.955308Z", "meta": {}, "account_number": "xxxxxx0001", "address": - {"city": null, "line2": null, "line1": null, "state": null, "postal_code": null, - "country_code": null}, "can_debit": false, "id": "BADLD5GZ8Uv8SXloInEFuKB"}' + "account_type": "checking", "name": "Alice G. Krebs", "links": {"customer": + "/customers/CU2qN2ov0SNIumvBA8iWOm81"}, "can_credit": true, "created_at": "2014-06-06T16:33:39.496116Z", + "fingerprint": "5f0ba9fa3f1122ef13b944a40abfe44e7eba9e16934e64200913cb4c402ace14", + "updated_at": "2014-06-06T16:33:39.496118Z", "meta": {}, "account_number": "xxxxxx0001", + "address": {"city": null, "line2": null, "line1": null, "state": null, "postal_code": + null, "country_code": null}, "can_debit": false, "id": "BA2s9HPLLimkYjd9Xufqeb3f"}' headers: !!python/object/apply:__builtin__.frozenset - - - !!python/tuple [Content-Length, !!python/unicode '581'] - - !!python/tuple [User-Agent, !!python/unicode 'balanced-python/1.0.1beta2'] - - !!python/tuple [Authorization, !!python/unicode 'Basic YWstdGVzdC1Xbnh5QTA2N1pYd0JsYW5xbWdOZm90bUFPeUpsRm5rZTpOb25l'] + - - !!python/tuple [User-Agent, !!python/unicode 'balanced-python/1.0.1beta2'] + - !!python/tuple [Content-Length, !!python/unicode '592'] - !!python/tuple [Content-Type, !!python/unicode 'application/json'] - !!python/tuple [Accept-Encoding, 'gzip, deflate, compress'] + - !!python/tuple [Authorization, !!python/unicode 'Basic YWstdGVzdC1Xbnh5QTA2N1pYd0JsYW5xbWdOZm90bUFPeUpsRm5rZTpOb25l'] - !!python/tuple [accept, !!python/unicode 'application/vnd.api+json;revision=1.1'] host: api.balancedpayments.com method: PUT - path: /bank_accounts/BADLD5GZ8Uv8SXloInEFuKB + path: /bank_accounts/BA2s9HPLLimkYjd9Xufqeb3f port: 443 protocol: https response: body: string: !!binary | - H4sIAAAAAAAAA5VTXW+bMBR9769APG+JbRwCeUuTdIrWpV2aSFOrCRlzWa0QExlTLYr47zMkFELT - qkOIh3su1+fj+nBlWXbI5CZgnKe51Jk9sp5M0bIO1dfAKs21kH8CmW9DUAa3HYLxkHoDbH+pm6oZ - km2hxB/GC+vHeDW7sybL2XS+staL+d2iaT6dFej9rurnz8A35oimo57EEsGhKSdCbkqGNTfDjueZ - TrdHXpP1RIWErn5qtlY3S9dPl9Pt7f3r/x2twQsoEQvOtEilmSrzJDnpKV6FcSYDriAS2nRolUOD - KGAaooCViE0Qpl+Ra94VIiMyHGGv5w8GDnIfG/6xEQlqp4Ss/hnEKGR+zJwYY0Igxk7oU8ooYmEM - lMIQDAzY9R0KLiUI+djhIeUUEcYB02Zwvos+4kJQz0XUGw5bXJ4VxCWJ/ln8/evx9HY6+PborV+8 - h19JOpezm/z7dXPUFjQrI2gsquNsFuRv9SCEWhvCokhB1k1P6P3J+dpXE5KJGcjlMn5bzrSR/ra8 - Sw2QBDyNLoDVrqt9G72YfARhFXzMkqxJXkSlce85VQ0qzPd3KakU09rZ88vWO25WaUo3h8NZLD0R - Ff26+ehUZ1K7/WyxPzm8fReyU9pdsq2r1q/vXdbvMK2B4vKQytFPcjr1/p/eysszNV2G71pV2Caw - 4qr4B7tFnpIWBQAA + H4sIAAAAAAAAA5VTW2+bMBR+769APG+JbVwS8pZ21RatS6Y11W6akDHHqxcuqTHRooj/PkPCDDSd + OoR48Hc4/i7nHC4cx41YtgkZ53mZ6cKdOd/NoeMcmq+BVV5qmf0MszKNQBnc9QjGEzq9xO6rtqjp + kbEUavxuvnQ+zNc3K+f6082bxdq5Xy5WS1t8uivU+21Tzx+Ab8wVtqLtNE8kB+ftyHmvICosnshs + U1NtSRqavCx0nh4JXt+TxyXJd+huuSjT3dV8Kj+v0qnlO1Ad7kBJITnTMs9M26xMkpOy6q9EzrKQ + K4ilNhValWARBUxDHLIacQnC9DXyzbvG/szzZl4wooGPsf/NChBGLqitklnzz6VAEQsE8wTGhIDA + XhRQyihikQBKYQIGBuwHHgWfEoQC7PGIcooI44CpbVxu439xoWhE6NRDkw6XBwWiJjHuDcL4ak6K + 4N3H21uZbr7+ioMvpXiEyBP2rhQ0q0OwHrXJ2ln53TwIoc6wsDhWUAzzk3p/sr411qRkggZy/hg/ + PS600f70eJsbIAl5Hp8Bm7FX+y56NvoYoiZ5wZLCRi/j2rlnrWo6Veb7o9ZUq+mMbX/xRsfZql0Z + JnHoBTOScTVui49WDTp1y3uj/cLm3W1ol25ItrNt43b1ivGAaQtUp5kZNGksfSGnU+3/6W287KkZ + MnzWqso1gVUX1R/fcKEyIgUAAA== headers: ["Content-Encoding: gzip\r\n", "Content-Type: application/json\r\n", - "Date: Fri, 06 Jun 2014 02:27:20 GMT\r\n", "Server: ngx_openresty/1.2.6.3\r\n", - "X-Balanced-Guru: OHM160903e8ed2211e3aecc02b12035401b\r\n", "X-Balanced-Host:\ + "Date: Fri, 06 Jun 2014 16:33:40 GMT\r\n", "Server: ngx_openresty/1.2.6.3\r\n", + "X-Balanced-Guru: OHM5108d222ed9811e3b7da06429171ffad\r\n", "X-Balanced-Host:\ \ bapi-integration-prod-8u30f7-10-3-4-8\r\n", "X-Balanced-Marketplace: TEST-MP1wTyxDAJKCaWPjBS4Oli4d\r\ \n", "X-Balanced-Merchant: TEST-MR1wiJbFKXFmlmVIyaPiduUV\r\n", "X-Balanced-Revision:\ - \ 1.1\r\n", "X-Balanced-Software-Build: 1.2.50\r\n", "Content-Length: 525\r\ + \ 1.1\r\n", "X-Balanced-Software-Build: 1.2.50\r\n", "Content-Length: 532\r\ \n", "Connection: keep-alive\r\n"] status: {code: 200, message: OK} - request: !!python/object:vcr.request.Request @@ -139,27 +139,27 @@ - !!python/tuple [User-Agent, !!python/unicode 'balanced-python/1.0.1beta2'] host: api.balancedpayments.com method: GET - path: /customers/CUCrb24TQtaUrFR69oRDmLP + path: /customers/CU2qN2ov0SNIumvBA8iWOm81 port: 443 protocol: https response: body: string: !!binary | - H4sIAAAAAAAAA41Uy27bMBC85ysEnZtIFgwJ9jVFTz20QXJpEAgrkoWISKTBRxDD0L93aUsyLapl - AcOH5cxwd3bE012SpMRqI3umdLpPXrGQJKfzPx4J6BlWhe26L1Ot4+LdQScQwrS0iiyBWKdMGy7A - cClGlVFkmNXsgYJhtAaDiLTIN9v7vMTfc17si2q/qR52ebEpy1/pTCGKRShVlZe7yqNQ2dS9FKZd - DsOpu/bx5VE1xfb5p4EX9e2p3Mmnr/33H9crD60Uy/nSVrHfjp3NBmZRIbSZtCBMrQ0Y62xMhbzv - wZD2elvPDDiDry65/o8M1LJ9oFQxvdgG4ea4AOIucG2sWC9vwrJrbzkwihwkHnQ1kXTlkEgrjDr6 - p8G6G6uxD63rtWRpLeoOtNkux2Q98C4o8ptQDXjXm8uIm9QL6DXeD3NM0wxdO2dWZ6d5e+P5MC7C - IxJQtG5lR88L8/btkTkdMg93Saun0YB4r4GcTYrI3EIDJcoaHpMYMStc/4v8iw/eZ7tiBvs0TAlM - wf8NE8KDppxtEUsukIBJuT5YwyLkGRXwjQKhgbgHKqJxgwx08CWwIjbDBFphf+DrC12kBcUmWKAg - Fb283/8I54gJuPic0mieJhB+X8Pd8Ae3UkW6NwYAAA== + H4sIAAAAAAAAA41Uy27bMBC85ysEnZPIllxX9S3JqZf00BYFWhTCmmQgIhLp8mHUMPTvXdqSTItK + GUDQYTkz5M4OebxJkpRYbWTLlE43yS8sJMnx9MclAS3DqrBNczvUGi5eHXQAIUxLq8gUiHXKtOEC + DJeiV+lFulHN7igYRiswiEjzxXJ1t1jj92253hTFpijvi3K9/lT8TEcKUSxCyT+uPhSlR6FyW7VS + mHraDKdu26fv+Z/nXO4XX58/23b/+FDyH1/acnnZc1dLMW0wrRV7cfRsdDCLK6HRpAZhKm3AWGdk + KuRdC4bUl+1aZsBZfPHJdXBgoKYNAKWK6ck8CDeHCRCngYNj+Xx5GZbd8aYdo8hO4kJTEUlnFom0 + wqiDvxoMfGs1nkPrai5bWouqAW1W0zZZC7wJivwqVh3u9dulxHXqRfQS8PsxqGmGrp1Sq7PjOL5+ + vesH4REJKFrVsqGngXkD98icdpmHO+fV09iCeK2AnEyKyFxDAyXKtjwm0WNmuP6dfMMH7+LOmMH+ + GqYEpuB9zYTw4FDOtoglZ0jApFzvrGER8ogK+EaB0EDcExXRuEIGOvgUWBHrYQDNsPf4/kITOYJi + AyxQkIqeX/D/hLPHBFx8UGk0TwMI71d30/0DZQmTSTkGAAA= headers: ["Content-Encoding: gzip\r\n", "Content-Type: application/json\r\n", - "Date: Fri, 06 Jun 2014 02:27:21 GMT\r\n", "Server: ngx_openresty/1.2.6.3\r\n", - "X-Balanced-Guru: OHM166d7396ed2211e3a4e806429171ffad\r\n", "X-Balanced-Host:\ - \ bapi-integration-prod-8u30f7-10-3-5-201\r\n", "X-Balanced-Marketplace: TEST-MP1wTyxDAJKCaWPjBS4Oli4d\r\ + "Date: Fri, 06 Jun 2014 16:33:41 GMT\r\n", "Server: ngx_openresty/1.2.6.3\r\n", + "X-Balanced-Guru: OHM5186cdb2ed9811e3ac8902b12035401b\r\n", "X-Balanced-Host:\ + \ bapi-integration-prod-8u30f7-10-3-4-8\r\n", "X-Balanced-Marketplace: TEST-MP1wTyxDAJKCaWPjBS4Oli4d\r\ \n", "X-Balanced-Merchant: TEST-MR1wiJbFKXFmlmVIyaPiduUV\r\n", "X-Balanced-Revision:\ - \ 1.1\r\n", "X-Balanced-Software-Build: 1.2.50\r\n", "Content-Length: 487\r\ + \ 1.1\r\n", "X-Balanced-Software-Build: 1.2.50\r\n", "Content-Length: 491\r\ \n", "Connection: keep-alive\r\n"] status: {code: 200, message: OK} - request: !!python/object:vcr.request.Request @@ -172,29 +172,29 @@ - !!python/tuple [User-Agent, !!python/unicode 'balanced-python/1.0.1beta2'] host: api.balancedpayments.com method: GET - path: /customers/CUCrb24TQtaUrFR69oRDmLP/bank_accounts?limit=10&offset=0 + path: /customers/CU2qN2ov0SNIumvBA8iWOm81/bank_accounts?limit=10&offset=0 port: 443 protocol: https response: body: string: !!binary | - H4sIAAAAAAAAA61UXWvbMBR9768wftjTlki28gllpEk6ytp0SxMYHcPI8vUq4thBlkND8H+fpNix - nSajDzXG4Pt57j2Hu7+yLNun8cqjjCVZLFN7aP1WRsvam69yiySTPP7rxdnaB6H8tutg3CP9DrY/ - l0GmRkzXoP1Po5n1MFpMH63xfDq5W1jL2d3jrAouenlytzHx7AXYSrWoIspKNOIMKnPE45VGWGJT - 6FiWymR9wDVejoXvkMVPSZfidt4dJPPJ+v7HMf9kVm8LgoecUcmTWFWNsygq5smPgzEae0xAwKWK - kCKDyiOASgg8qj22gzD5grrqXSBn6PSGuN8adDou6j5X+EM1JIiN4LHJ6YTIp4OQuiHGjgMhdv0B - IZQg6odACPRAuQF3By6BLnEQGmCX+YQR5FAGmFSFs03wPywOanUR6fd6NSwvAkINot2gv30zmtxP - Ot+e+8tt/+lXlNzF09vs+03Vag2SagqqFZV0VgJ5NQ9CqKYQGgQC0lP2uNwVmy/3qkhSNINz3ozf - mlOpRn9r3iTKEXksCc44jdbFru49y3wAviE+pFFaMc8DvbhLmzKFcvX9o0eyy30Zsx3R1FDfLnWb - ti+otsnL14ivubzG6FMShinIa1QwYsfwqitq8R5WaB+Z/bAeprlqglHR4gBCWUrDRsCWJ5mmt4ZE - JooCnVekhVx82Phqn0aCWi21o9C8Zi1DoEZ1qvN9Q/YtHuTtIrbA2vTX/xp3452166cmLalrtqhf - spo8ToCWUfmFIuZUvRPU4a5dgHNxYrPMxjztE4wXU3Nbs3aV/wN20hiEdwYAAA== + H4sIAAAAAAAAA61UbWvbMBD+3l9h/GGftkSy1SQOlJF2ZSvrkrGm7I1hZPm0avFLKsuhIeS/T1Ls + 2k6T0UFDMPie891z9zzc5sRx3Ihmi5AylpeZKtyx81MHHWdjnxqWealE9jvMyjQCqXHX9zAektEp + dl/XSbZGRlMw+M1k6nyazC9nzsWXy3dXc+d2ejWbNslVr1Ctlzaf3QFb6BZNRl1pkggGzvue81FC + VDR4IrKFoVqT1DRZWag83RG8uPXup16+QjfTqzJdnU9G4ussHTV896YOVyAFF4wqkWe6bFYmSTXZ + 9nFERrOQSYiF0hlKltAgEqiCOKQGcT2EyRs00P85Hox9f+wHPRIMMB78aAbgelyQSyky+80pRxEN + OPU5xp4HHPtRQAgliEYcCIEhaBjwIPAJDIiHUIB9FhFGkEcZYNIULpfxv7gQ1PPIyEfDFpc7CdyQ + 6HeM0D+feEXw4fP1tUgX3//EwbeS30Pk86ZXCooaEZod1co2XnmwP4RQyyw0jiUU+/oJta5WXy9W + q6SFBu9wGD8NF0rP/jS8zDWQhCyPD4DW9nLdRg9KH0Nklec0KRrpRWw2d3RVttJWP3+Zmdx6YTbs + JrSw4vdr6xb9Y8btSvM2EalQZxi9yjkvQJ2hShM3gwdT0vh3t0T3UdyXa2K76y4YVT12LHSkDiwl + rEReGoVbVFSuVTDfVZ9xIV9uAXql1obGMa3T0D1uPSuiobVv9k3H+z0Rb/tVbkW2i7ffOsfjmbXb + 96Y+a3tU2/es5ZA9onXWtnZAl+fuXj2TVJ38fxPbZXbm6e9xPLqsrWtUO9n+BfdCL06GBgAA headers: ["Content-Encoding: gzip\r\n", "Content-Type: application/json\r\n", - "Date: Fri, 06 Jun 2014 02:27:21 GMT\r\n", "Server: ngx_openresty/1.2.6.3\r\n", - "X-Balanced-Guru: OHM16bd64f0ed2211e3ac8902b12035401b\r\n", "X-Balanced-Host:\ - \ bapi-integration-prod-8u30f7-10-3-5-201\r\n", "X-Balanced-Marketplace: TEST-MP1wTyxDAJKCaWPjBS4Oli4d\r\ + "Date: Fri, 06 Jun 2014 16:33:41 GMT\r\n", "Server: ngx_openresty/1.2.6.3\r\n", + "X-Balanced-Guru: OHM51d1db18ed9811e38ad106429171ffad\r\n", "X-Balanced-Host:\ + \ bapi-integration-prod-8u30f7-10-3-4-8\r\n", "X-Balanced-Marketplace: TEST-MP1wTyxDAJKCaWPjBS4Oli4d\r\ \n", "X-Balanced-Merchant: TEST-MR1wiJbFKXFmlmVIyaPiduUV\r\n", "X-Balanced-Revision:\ - \ 1.1\r\n", "X-Balanced-Software-Build: 1.2.50\r\n", "Content-Length: 616\r\ + \ 1.1\r\n", "X-Balanced-Software-Build: 1.2.50\r\n", "Content-Length: 624\r\ \n", "Connection: keep-alive\r\n"] status: {code: 200, message: OK} - request: !!python/object:vcr.request.Request @@ -208,27 +208,27 @@ - !!python/tuple [accept, !!python/unicode 'application/vnd.api+json;revision=1.1'] host: api.balancedpayments.com method: POST - path: /bank_accounts/BADLD5GZ8Uv8SXloInEFuKB/credits + path: /bank_accounts/BA2s9HPLLimkYjd9Xufqeb3f/credits port: 443 protocol: https response: body: {string: "{\n \"credits\": [\n {\n \"status\": \"succeeded\",\n\ \ \"description\": \"alice\",\n \"links\": {\n \"customer\"\ - : \"CUCrb24TQtaUrFR69oRDmLP\",\n \"destination\": \"BADLD5GZ8Uv8SXloInEFuKB\"\ - ,\n \"order\": null\n },\n \"updated_at\": \"2014-06-06T02:27:22.635298Z\"\ - ,\n \"created_at\": \"2014-06-06T02:27:22.431185Z\",\n \"transaction_number\"\ - : \"CR813-132-7215\",\n \"failure_reason\": null,\n \"currency\"\ + : \"CU2qN2ov0SNIumvBA8iWOm81\",\n \"destination\": \"BA2s9HPLLimkYjd9Xufqeb3f\"\ + ,\n \"order\": null\n },\n \"updated_at\": \"2014-06-06T16:33:42.493090Z\"\ + ,\n \"created_at\": \"2014-06-06T16:33:42.183974Z\",\n \"transaction_number\"\ + : \"CR937-364-6500\",\n \"failure_reason\": null,\n \"currency\"\ : \"USD\",\n \"amount\": 1000,\n \"failure_reason_code\": null,\n\ - \ \"meta\": {},\n \"href\": \"/credits/CRHG1lNaScFGB1lgZC2azOx\"\ - ,\n \"appears_on_statement_as\": \"example.com\",\n \"id\": \"CRHG1lNaScFGB1lgZC2azOx\"\ + \ \"meta\": {},\n \"href\": \"/credits/CR2vaCzFWKJc9Ngzf4hRRmbX\"\ + ,\n \"appears_on_statement_as\": \"example.com\",\n \"id\": \"CR2vaCzFWKJc9Ngzf4hRRmbX\"\ \n }\n ],\n \"links\": {\n \"credits.order\": \"/orders/{credits.order}\"\ ,\n \"credits.customer\": \"/customers/{credits.customer}\",\n \"credits.destination\"\ : \"/resources/{credits.destination}\",\n \"credits.reversals\": \"/credits/{credits.id}/reversals\"\ ,\n \"credits.events\": \"/credits/{credits.id}/events\"\n }\n}"} - headers: ["Content-Type: application/json\r\n", "Date: Fri, 06 Jun 2014 02:27:22\ - \ GMT\r\n", "Server: ngx_openresty/1.2.6.3\r\n", "X-Balanced-Guru: OHM172078f6ed2211e3aecc02b12035401b\r\ - \n", "X-Balanced-Host: bapi-integration-prod-8u30f7-10-3-5-201\r\n", "X-Balanced-Marketplace:\ + headers: ["Content-Type: application/json\r\n", "Date: Fri, 06 Jun 2014 16:33:42\ + \ GMT\r\n", "Server: ngx_openresty/1.2.6.3\r\n", "X-Balanced-Guru: OHM52369bc0ed9811e3aecc02b12035401b\r\ + \n", "X-Balanced-Host: bapi-integration-prod-8u30f7-10-3-4-8\r\n", "X-Balanced-Marketplace:\ \ TEST-MP1wTyxDAJKCaWPjBS4Oli4d\r\n", "X-Balanced-Merchant: TEST-MR1wiJbFKXFmlmVIyaPiduUV\r\ \n", "X-Balanced-Revision: 1.1\r\n", "X-Balanced-Software-Build: 1.2.50\r\n", - "Content-Length: 951\r\n", "Connection: keep-alive\r\n"] + "Content-Length: 955\r\n", "Connection: keep-alive\r\n"] status: {code: 201, message: CREATED} From 7f04c976cd666ab127598a4e24bf4e350fae1e10 Mon Sep 17 00:00:00 2001 From: Chad Whitacre Date: Fri, 6 Jun 2014 14:09:14 -0400 Subject: [PATCH 10/23] Prune vestigial disbursement strategy I'm not going to implement this now. --- gittip/models/participant.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/gittip/models/participant.py b/gittip/models/participant.py index c7417b6ad4..144dc0dda0 100644 --- a/gittip/models/participant.py +++ b/gittip/models/participant.py @@ -262,8 +262,6 @@ def close(self, disbursement_strategy): pass # No balance, supposedly. archive will check. elif disbursement_strategy == 'bank': self.withdraw_balance_to_bank_account(cursor) - elif disbursement_strategy == 'upstream': - self.refund_to_patrons(cursor) elif disbursement_strategy == 'downstream': # This in particular needs to come before clear_tips_giving. self.distribute_balance_as_final_gift(cursor) @@ -303,10 +301,6 @@ def withdraw_balance_to_bank_account(self, cursor): ) # XXX Records the exchange using a different cursor. :-/ - def refund_balance_to_patrons(self, cursor): - raise NotImplementedError - - class NoOneToGiveFinalGiftTo(Exception): pass def distribute_balance_as_final_gift(self, cursor): From a4fe44a0b07d53cb2b91e5515d505e0871a8f096 Mon Sep 17 00:00:00 2001 From: Changaco Date: Sat, 7 Jun 2014 13:33:59 +0200 Subject: [PATCH 11/23] remove vestigial file --- js/gittip/close.js | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 js/gittip/close.js diff --git a/js/gittip/close.js b/js/gittip/close.js deleted file mode 100644 index 909709e4d6..0000000000 --- a/js/gittip/close.js +++ /dev/null @@ -1,4 +0,0 @@ -Gittip.close = {}; - -Gittip.close.init = function() { -} From b482f9b129b0b77787558212c0535340e96bbcf2 Mon Sep 17 00:00:00 2001 From: Changaco Date: Sat, 7 Jun 2014 13:34:51 +0200 Subject: [PATCH 12/23] update FAQ --- www/about/faq.html.spt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/www/about/faq.html.spt b/www/about/faq.html.spt index 5d1ec71dd2..9fbb6d6302 100644 --- a/www/about/faq.html.spt +++ b/www/about/faq.html.spt @@ -151,11 +151,9 @@ title = "Frequently Asked Questions" buttons to others on your profile. -
How do I cancel my account?
+
How do I close my account?
-
Sorry that this is manual right now. Email us - and we'll take care of it for you.
+
Close account
From b8385a2157e27cdfc3dd624bc6f5591ef1f03bf0 Mon Sep 17 00:00:00 2001 From: Changaco Date: Sat, 7 Jun 2014 14:09:23 +0200 Subject: [PATCH 13/23] show a message instead of redirecting to homepage --- www/about/me/%redirect_to.spt | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/www/about/me/%redirect_to.spt b/www/about/me/%redirect_to.spt index 1ef552b49d..2f0e258a23 100644 --- a/www/about/me/%redirect_to.spt +++ b/www/about/me/%redirect_to.spt @@ -1,5 +1,11 @@ [---] -if user.ANON: - request.redirect('/') -request.redirect(u'/' + user.participant.username + u'/' + path['redirect_to']) -[---] text/plain +if not user.ANON: + request.redirect('/' + user.participant.username + '/' + path['redirect_to']) +title = "Sign In" +[---] text/html +{% extends "templates/base.html" %} +{% block box %} +
+

You need to sign in to access this page.

+
+{% endblock %} From 376f939fd45dc07f3953bd48e1bb7093a9f1d747 Mon Sep 17 00:00:00 2001 From: Chad Whitacre Date: Tue, 10 Jun 2014 10:19:45 -0400 Subject: [PATCH 14/23] Tweak language on close page --- www/%username/account/close.spt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/www/%username/account/close.spt b/www/%username/account/close.spt index b7658890ee..56ead13bbc 100644 --- a/www/%username/account/close.spt +++ b/www/%username/account/close.spt @@ -52,10 +52,9 @@ if POST:

Username

We may give your username to someone else if they ask for - it, but not for at least a year after you close your account.

- -

Though if we determine that you've been infringing a - trademark, we may give your username away sooner.

+ it, but not for at least a year after you close your account + (unless we determine that you've been infringing a + trademark).

Until we give your username to someone else, you can use it again if you ever decide to rejoin Gittip. Simply sign in.

From ce89b1fdbdac05bce38ba88b450b03bdb0547cb9 Mon Sep 17 00:00:00 2001 From: Chad Whitacre Date: Tue, 10 Jun 2014 13:16:37 -0400 Subject: [PATCH 15/23] Dial back language on Close to be more accurate We're not able to deliver on strong "we delete all your info" promises yet, because we haven't formalized a data retention policy (#397). --- www/%username/account/close.spt | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/www/%username/account/close.spt b/www/%username/account/close.spt index 56ead13bbc..941b029914 100644 --- a/www/%username/account/close.spt +++ b/www/%username/account/close.spt @@ -62,16 +62,20 @@ if POST:

Personal Information

-

Almost all of your personal information will be deleted - immediately, including your "making the world better" - statement, any funding goal, the tips you're receiving, the - tips you're giving, any bank account or credit card, and any - linked accounts that aren't used for sign-in. You'll also be +

We immediately clear out most of the information in your + profile (though it may still exist in our database in event + logs, to be cleared eventually once we sort out our data + retention policy).

+ +

Things we clear immediately include your “making the + world better” statement, any funding goal, the tips + you're receiving, and those you're giving. You'll also be removed from any communities and teams you were a part of. If - you're closing a team account, then all team members will be - removed from the team.

+ you're closing a team account, all team members will be removed + from the team.

-

What we don't delete is your past giving and +

We specifically don't delete your past giving and receiving history on the site, because that information also belongs equally to other users (the ones you gave to and received from).

From 8541ed294fe95ee8257f4d8cb9c3a85bab19c4b6 Mon Sep 17 00:00:00 2001 From: Chad Whitacre Date: Wed, 11 Jun 2014 12:37:47 -0400 Subject: [PATCH 16/23] Tweak language on close page --- www/%username/account/close.spt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/%username/account/close.spt b/www/%username/account/close.spt index 941b029914..6ffe9fb6be 100644 --- a/www/%username/account/close.spt +++ b/www/%username/account/close.spt @@ -64,7 +64,7 @@ if POST:

We immediately clear out most of the information in your profile (though it may still exist in our database in event - logs, to be cleared eventually once we sort out our data retention policy).

From 607af9b77bd335d5a7dfeb64d432e3a9d95160e5 Mon Sep 17 00:00:00 2001 From: Chad Whitacre Date: Wed, 11 Jun 2014 12:40:37 -0400 Subject: [PATCH 17/23] Prune unused variables These were actually using API that went away in #2352, but they're also unused in the simplate anyway. --- www/%username/account/close.spt | 3 --- 1 file changed, 3 deletions(-) diff --git a/www/%username/account/close.spt b/www/%username/account/close.spt index 6ffe9fb6be..3c97bcfacd 100644 --- a/www/%username/account/close.spt +++ b/www/%username/account/close.spt @@ -6,9 +6,6 @@ title = participant.username # used in the title tag username = participant.username # used in footer shared with on/$platform/ # pages -can_upstream = participant.get_dollars_receiving() > 0 -can_downstream = participant.get_dollars_giving() > 0 - if POST: disbursement_strategy = request.body.get('disbursement_strategy') participant.close(disbursement_strategy) From a8195015c46879e8a9f6471a9b3df5f249bf4892 Mon Sep 17 00:00:00 2001 From: Chad Whitacre Date: Wed, 11 Jun 2014 14:26:39 -0400 Subject: [PATCH 18/23] Implement "Account closed" page as 410 --- 410.spt | 21 +++++++++++++++++++++ gittip/utils/__init__.py | 3 +++ 2 files changed, 24 insertions(+) create mode 100644 410.spt diff --git a/410.spt b/410.spt new file mode 100644 index 0000000000..5707944f41 --- /dev/null +++ b/410.spt @@ -0,0 +1,21 @@ +[---] +[---] text/html via jinja2 +{% extends "templates/base.html" %} +{% block heading %}

Closed

{% endblock %} +{% block box %} + +
+ +

The account owner has closed this account.

+ + {% if user.ANON %} +

Are you the account owner?

+ +

{% include "templates/sign-in-using.html" %} to reopen your account.

+ {% endif %} + +
+ + + +{% endblock %} diff --git a/gittip/utils/__init__.py b/gittip/utils/__init__.py index a8711d9d86..feeb647d3a 100644 --- a/gittip/utils/__init__.py +++ b/gittip/utils/__init__.py @@ -352,6 +352,9 @@ def get_participant(request, restrict=True): canonicalize(request.line.uri.path.raw, '/', participant.username, slug, qs) + if participant.is_closed: + raise Response(410) + if participant.claimed_time is None: # This is a stub participant record for someone on another platform who From 1b2ebc1aa71b2b42351d9945df1d43eb279d7fef Mon Sep 17 00:00:00 2001 From: Chad Whitacre Date: Wed, 11 Jun 2014 14:29:16 -0400 Subject: [PATCH 19/23] Redirect to "Account closed" page after closing --- www/%username/account/close.spt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/www/%username/account/close.spt b/www/%username/account/close.spt index 3c97bcfacd..5c097c7d5e 100644 --- a/www/%username/account/close.spt +++ b/www/%username/account/close.spt @@ -9,7 +9,7 @@ username = participant.username # used in footer shared with on/$platform/ if POST: disbursement_strategy = request.body.get('disbursement_strategy') participant.close(disbursement_strategy) - request.redirect('/') + request.redirect('/%s/' % participant.username) [---] text/html {% extends "templates/base.html" %} @@ -78,7 +78,7 @@ if POST: received from).

After you close your account, your profile page will say, - "Account closed by user."

+ “The account owner has closed this account.”

Remember

From 34228d9bbfdb13961b283c1ba1094f2cffba64f8 Mon Sep 17 00:00:00 2001 From: Chad Whitacre Date: Wed, 11 Jun 2014 14:31:14 -0400 Subject: [PATCH 20/23] Lead with "Personal information" on close page --- www/%username/account/close.spt | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/www/%username/account/close.spt b/www/%username/account/close.spt index 5c097c7d5e..6c5b58fa70 100644 --- a/www/%username/account/close.spt +++ b/www/%username/account/close.spt @@ -46,17 +46,6 @@ if POST: {% endif %} -

Username

- -

We may give your username to someone else if they ask for - it, but not for at least a year after you close your account - (unless we determine that you've been infringing a - trademark).

- -

Until we give your username to someone else, you can use it - again if you ever decide to rejoin Gittip. Simply sign in.

- -

Personal Information

We immediately clear out most of the information in your @@ -81,6 +70,17 @@ if POST: “The account owner has closed this account.”

+

Username

+ +

We may give your username to someone else if they ask for + it, but not for at least a year after you close your account + (unless we determine that you've been infringing a + trademark).

+ +

Until we give your username to someone else, you can use it + again if you ever decide to rejoin Gittip. Simply sign in.

+ +

Remember

We have no control over links to your profile from other From 676646b8da140617838a75f8618dd046c2b161f1 Mon Sep 17 00:00:00 2001 From: Chad Whitacre Date: Wed, 11 Jun 2014 14:40:44 -0400 Subject: [PATCH 21/23] Fix a couple regressions from merging or sumthin' --- tests/py/test_close.py | 2 +- www/%username/account/close.spt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/py/test_close.py b/tests/py/test_close.py index a830286ccc..731878ce82 100644 --- a/tests/py/test_close.py +++ b/tests/py/test_close.py @@ -22,7 +22,7 @@ def test_close_closes(self): alice.set_tip_to(bob, D('3.00')) carl.set_tip_to(alice, D('2.00')) - alice.cancel('downstream') + alice.close('downstream') assert carl.get_tip_to('alice') == 0 assert alice.balance == 0 diff --git a/www/%username/account/close.spt b/www/%username/account/close.spt index 6c5b58fa70..fedbd702aa 100644 --- a/www/%username/account/close.spt +++ b/www/%username/account/close.spt @@ -33,7 +33,7 @@ if POST:

  • + value="downstream" {{ 'disabled' if not participant.giving }} />
  • From 886714fd3293c889993852ccfbb3bfe387fa4a67 Mon Sep 17 00:00:00 2001 From: Chad Whitacre Date: Thu, 12 Jun 2014 10:13:19 -0400 Subject: [PATCH 22/23] Disallow account closing during payday --- tests/py/test_close.py | 31 +++++++++++++++++++++++++++++++ www/%username/account/close.spt | 33 ++++++++++++++++++++++++++++----- 2 files changed, 59 insertions(+), 5 deletions(-) diff --git a/tests/py/test_close.py b/tests/py/test_close.py index 731878ce82..bf170e33c8 100644 --- a/tests/py/test_close.py +++ b/tests/py/test_close.py @@ -5,6 +5,7 @@ import balanced import pytest +from gittip.billing.payday import Payday from gittip.models.community import Community from gittip.models.participant import Participant from gittip.testing import Harness @@ -32,6 +33,36 @@ def test_close_raises_for_unknown_disbursement_strategy(self): with pytest.raises(alice.UnknownDisbursementStrategy): alice.close('cheese') + def test_close_page_is_usually_available(self): + self.make_participant('alice', claimed_time='now') + body = self.client.GET('/alice/account/close', auth_as='alice').body + assert 'Personal Information' in body + + def test_close_page_is_not_available_during_payday(self): + Payday(self.db).start() + self.make_participant('alice', claimed_time='now') + body = self.client.GET('/alice/account/close', auth_as='alice').body + assert 'Personal Information' not in body + assert 'Try Again Later' in body + + def test_can_post_to_close_page(self): + alice = self.make_participant('alice', claimed_time='now', balance=7) + bob = self.make_participant('bob', claimed_time='now') + alice.set_tip_to(bob, D('10.00')) + + data = {'disbursement_strategy': 'downstream'} + response = self.client.PxST('/alice/account/close', auth_as='alice', data=data) + assert response.code == 302 + assert response.headers['Location'] == '/alice/' + assert Participant.from_username('alice').balance == 0 + assert Participant.from_username('bob').balance == 7 + + def test_cant_post_to_close_page_during_payday(self): + Payday(self.db).start() + self.make_participant('alice', claimed_time='now') + body = self.client.POST('/alice/account/close', auth_as='alice').body + assert 'Try Again Later' in body + # wbtba - withdraw_balance_to_bank_account diff --git a/www/%username/account/close.spt b/www/%username/account/close.spt index fedbd702aa..983a1cab87 100644 --- a/www/%username/account/close.spt +++ b/www/%username/account/close.spt @@ -6,10 +6,19 @@ title = participant.username # used in the title tag username = participant.username # used in footer shared with on/$platform/ # pages +payday_is_running = website.db.one(""" + + SELECT ts_start FROM paydays WHERE ts_end='1970-01-01T00:00:00+00'::timestamptz + +""") is not None + if POST: - disbursement_strategy = request.body.get('disbursement_strategy') - participant.close(disbursement_strategy) - request.redirect('/%s/' % participant.username) + if payday_is_running: + pass # User will get the "Try Again Later" message. + else: + disbursement_strategy = request.body.get('disbursement_strategy') + participant.close(disbursement_strategy) + request.redirect('/%s/' % participant.username) [---] text/html {% extends "templates/base.html" %} @@ -19,11 +28,24 @@ if POST: {% block box %}
    + {% if payday_is_running %} + +
    + +

    Try Again Later

    + +

    Sorry, we're running payday right now, and we're not set up + to close accounts while payday is running. Please check back in + a few hours.

    + +
    + + {% else %}
    -
    +
    {% if participant.balance > 0 %} -

    Balance

    +

    Balance

    You have a balance of ${{ participant.balance }}.
    What should we do with it?

    @@ -99,5 +121,6 @@ if POST: type="submit">Yes, Close My Gittip Account
    + {% endif %}
    {% endblock %} From 394e9b8fbfac60c2ef6be0472905cf1747b411df Mon Sep 17 00:00:00 2001 From: Chad Whitacre Date: Fri, 13 Jun 2014 14:37:28 -0400 Subject: [PATCH 23/23] Add guards to disbursement options Simple conditionals with instructions in non-available cases. Good enough for now? --- gittip/models/participant.py | 8 +++++++ www/%username/account/close.spt | 39 ++++++++++++++++++++++++++++----- 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/gittip/models/participant.py b/gittip/models/participant.py index 075c144ca6..8e4d76015f 100644 --- a/gittip/models/participant.py +++ b/gittip/models/participant.py @@ -149,6 +149,14 @@ def set_session_expires(self, expires): self.set_attributes(session_expires=expires) + # Suspiciousness + # ============== + + @property + def is_whitelisted(self): + return self.is_suspicious is False + + # Claimed-ness # ============ diff --git a/www/%username/account/close.spt b/www/%username/account/close.spt index 983a1cab87..8fce29ffdb 100644 --- a/www/%username/account/close.spt +++ b/www/%username/account/close.spt @@ -50,14 +50,41 @@ if POST: What should we do with it?

      -
    • -
    • + + {% if participant.last_ach_result == '' %} + {% if participant.is_whitelisted %} +
    • +
    • + {% else %} + +
    • + + {% endif %} + {% else %} +
    • + {% endif %} + + {% if participant.giving %}
    • + value="downstream" />
    • + tip + {% else %} +
    • Go set up some tips to be able to distribute your + balance as a final gift.
    • + {% endif %} +

    If neither option works for you, please