From 98070d07902a5554f56e4ccc04d06f870f4f1338 Mon Sep 17 00:00:00 2001 From: Rohit Paul Kuruvilla Date: Fri, 8 May 2015 15:48:08 +0530 Subject: [PATCH 01/25] Add braintree to vendor/ --- requirements.txt | 2 ++ vendor/braintree-3.14.0.tar.gz | Bin 0 -> 53408 bytes 2 files changed, 2 insertions(+) create mode 100644 vendor/braintree-3.14.0.tar.gz diff --git a/requirements.txt b/requirements.txt index 8bf2acb058..dea85263f0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -48,3 +48,5 @@ pytz==2014.7 Babel==1.3 misaka==1.0.2 + +braintree==3.14.0 diff --git a/vendor/braintree-3.14.0.tar.gz b/vendor/braintree-3.14.0.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..4507f62874141d25826554d61fb4720f31be0e1d GIT binary patch literal 53408 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

uvQ7}O6?is$i#fuL@Em~ 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 literal 0 HcmV?d00001 From 4cd8c0e790966c88c2083f6ceeabf41eefba66a4 Mon Sep 17 00:00:00 2001 From: Rohit Paul Kuruvilla Date: Fri, 8 May 2015 17:21:26 +0530 Subject: [PATCH 02/25] wireup braintree --- defaults.env | 5 +++++ gratipay/wireup.py | 16 ++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/defaults.env b/defaults.env index 4a28b02249..0a90126162 100644 --- a/defaults.env +++ b/defaults.env @@ -12,6 +12,11 @@ GRATIPAY_COMPRESS_ASSETS=no BALANCED_API_SECRET=ak-test-2zFgxfVijBzn4xC9dLRtLkxoB38iNKNKR +BRAINTREE_SANDBOX_MODE=true +BRAINTREE_MERCHANT_ID=ddnq29fv74cqxwkg +BRAINTREE_PUBLIC_KEY=f9xk4pb5ts86k67k +BRAINTREE_PRIVATE_KEY=36ded60b7f4a43ebc605ca5f4b33d909 + COINBASE_API_KEY=uETKVUrnPuXzVaVj COINBASE_API_SECRET=32zAkQCcHHYkGGn29VkvEZvn21PM1lgO diff --git a/gratipay/wireup.py b/gratipay/wireup.py index c3b11ef193..bd1196b684 100644 --- a/gratipay/wireup.py +++ b/gratipay/wireup.py @@ -13,6 +13,7 @@ from babel.messages.pofile import read_po from babel.numbers import parse_pattern import balanced +import braintree import gratipay import gratipay.billing.payday import raven @@ -68,6 +69,17 @@ def mail(env, project_root='.'): def billing(env): balanced.configure(env.balanced_api_secret) + if env.braintree_sandbox_mode: + braintree_env = braintree.Environment.Sandbox + else: + braintree_env = braintree.Environment.Production + + braintree.Configuration.configure( + braintree_env, + env.braintree_merchant_id, + env.braintree_public_key, + env.braintree_private_key + ) def username_restrictions(website): gratipay.RESTRICTED_USERNAMES = os.listdir(website.www_root) @@ -344,6 +356,10 @@ def env(): GRATIPAY_CACHE_STATIC = is_yesish, GRATIPAY_COMPRESS_ASSETS = is_yesish, BALANCED_API_SECRET = unicode, + BRAINTREE_SANDBOX_MODE = is_yesish, + BRAINTREE_MERCHANT_ID = unicode, + BRAINTREE_PUBLIC_KEY = unicode, + BRAINTREE_PRIVATE_KEY = unicode, GITHUB_CLIENT_ID = unicode, GITHUB_CLIENT_SECRET = unicode, GITHUB_CALLBACK = unicode, From a64aca19c98180c2dcb851506012eb6c8ac4cc28 Mon Sep 17 00:00:00 2001 From: Rohit Paul Kuruvilla Date: Fri, 8 May 2015 17:58:42 +0530 Subject: [PATCH 03/25] Load Balanced JS for bank form, and Braintree for cc --- js/gratipay/payments.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/js/gratipay/payments.js b/js/gratipay/payments.js index 927c6e3cd6..1f85d98450 100644 --- a/js/gratipay/payments.js +++ b/js/gratipay/payments.js @@ -15,10 +15,10 @@ Gratipay.payments = {}; Gratipay.payments.init = function() { $('#delete').submit(Gratipay.payments.deleteRoute); +} - // Lazily depend on Balanced. - var balanced_js = "https://js.balancedpayments.com/1.1/balanced.min.js"; - jQuery.getScript(balanced_js, function() { +Gratipay.payments.lazyLoad = function(script_url) { + jQuery.getScript(script_url, function() { $('input[type!="hidden"]').eq(0).focus(); }).fail(Gratipay.error); } @@ -91,7 +91,11 @@ Gratipay.payments.ba = {}; Gratipay.payments.ba.init = function() { Gratipay.payments.init(); - $('form#bank-account').submit(Gratipay.payments.ba.submit); + + // Lazily depend on Balanced. + Gratipay.payments.lazyLoad("https://js.balancedpayments.com/1.1/balanced.min.js") + + $('form#bank-account').submit(Gratipay.payments.ba.submit); }; Gratipay.payments.ba.submit = function (e) { @@ -131,6 +135,10 @@ Gratipay.payments.cc = {}; Gratipay.payments.cc.init = function() { Gratipay.payments.init(); + + // Lazily depend on Braintree. + Gratipay.payments.lazyLoad("https://js.braintreegateway.com/v2/braintree.js") + $('form#credit-card').submit(Gratipay.payments.cc.submit); Gratipay.payments.cc.formatInputs( $('#card_number'), From 1301707ac10e72ed15a907becf44f8b7228bba33 Mon Sep 17 00:00:00 2001 From: Rohit Paul Kuruvilla Date: Fri, 8 May 2015 21:10:25 +0530 Subject: [PATCH 04/25] Add 'braintree-cc' as a payment network --- sql/branch.sql | 1 + 1 file changed, 1 insertion(+) create mode 100644 sql/branch.sql diff --git a/sql/branch.sql b/sql/branch.sql new file mode 100644 index 0000000000..3dde6b0388 --- /dev/null +++ b/sql/branch.sql @@ -0,0 +1 @@ +ALTER TYPE payment_net ADD VALUE 'braintree-cc'; \ No newline at end of file From 4260b3d6783c8deabd785c84faf8bdceb7bd42fc Mon Sep 17 00:00:00 2001 From: Rohit Paul Kuruvilla Date: Fri, 8 May 2015 22:32:03 +0530 Subject: [PATCH 05/25] Handle braintree nonce in routes/associate.json.spt --- www/%username/routes/associate.json.spt | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/www/%username/routes/associate.json.spt b/www/%username/routes/associate.json.spt index 080e2a5556..9389723748 100644 --- a/www/%username/routes/associate.json.spt +++ b/www/%username/routes/associate.json.spt @@ -2,6 +2,7 @@ from __future__ import absolute_import, division, print_function, unicode_litera from aspen import Response import balanced +import braintree from gratipay.billing.exchanges import repr_exception from gratipay.models.exchange_route import ExchangeRoute @@ -24,10 +25,10 @@ if change: if not address: raise Response(400, _("The address cannot be empty.")) - if network.startswith('balanced-'): + if network == 'balanced-ba': account = participant.get_balanced_account() - if network == 'balanced-ba' and account.merchant_status != 'underwritten': + if account.merchant_status != 'underwritten': raise Response(400, _("You need to verify your identity first.")) try: @@ -35,6 +36,18 @@ if change: except balanced.exc.HTTPError as err: raise Response(400, repr_exception(err)) + elif network == 'braintree-cc': + result = braintree.PaymentMethod.create({ + "customer_id": participant.id, + "payment_method_nonce": address, + "options": {"verify_card": True} + }) + + if result.is_success: + ExchangeRoute.insert(participant, network, result.payment_method.token) + else: + raise Response(400, result.errors.deep_errors[0].message) + elif network == 'bitcoin': if not bitcoin.validate(address): raise Response(400, _("This is not a valid Bitcoin address.")) From 6b365dc15cd571a4b456972e2e385a39b570c0e5 Mon Sep 17 00:00:00 2001 From: Rohit Paul Kuruvilla Date: Fri, 8 May 2015 22:46:43 +0530 Subject: [PATCH 06/25] Modify payments.cc.* to handle braintree --- js/gratipay/payments.js | 108 +++++++++++++++------------------------- 1 file changed, 39 insertions(+), 69 deletions(-) diff --git a/js/gratipay/payments.js b/js/gratipay/payments.js index 1f85d98450..43a326d4a2 100644 --- a/js/gratipay/payments.js +++ b/js/gratipay/payments.js @@ -43,37 +43,16 @@ Gratipay.payments.deleteRoute = function(e) { return false; }; -Gratipay.payments.onError = function(response) { - $('button#save').prop('disabled', false); - var msg = response.status_code + ": " + - $.map(response.errors, function(obj) { return obj.description }).join(', '); - Gratipay.notification(msg, 'error', -1); - return msg; -}; - Gratipay.payments.onSuccess = function(data) { $('button#save').prop('disabled', false); window.location.reload(); }; -Gratipay.payments.associate = function (network) { return function (response) { - if (response.status_code !== 201) { - return Gratipay.payments.onError(response); - } - - /* The request to tokenize the thing succeeded. Now we need to associate it - * to the Customer on Balanced and to the participant in our DB. - */ - var data = { - network: network, - address: network == 'balanced-ba' ? response.bank_accounts[0].href - : response.cards[0].href, - }; - +Gratipay.payments.associate = function (network, address) { jQuery.ajax({ url: "associate.json", type: "POST", - data: data, + data: {network: network, address: address}, dataType: "json", success: Gratipay.payments.onSuccess, error: [ @@ -81,7 +60,7 @@ Gratipay.payments.associate = function (network) { return function (response) { function() { $('button#save').prop('disabled', false); }, ], }); -}}; +}; // Bank Accounts @@ -93,9 +72,9 @@ Gratipay.payments.ba.init = function() { Gratipay.payments.init(); // Lazily depend on Balanced. - Gratipay.payments.lazyLoad("https://js.balancedpayments.com/1.1/balanced.min.js") + Gratipay.payments.lazyLoad("https://js.balancedpayments.com/1.1/balanced.min.js") - $('form#bank-account').submit(Gratipay.payments.ba.submit); + $('form#bank-account').submit(Gratipay.payments.ba.submit); }; Gratipay.payments.ba.submit = function (e) { @@ -122,9 +101,24 @@ Gratipay.payments.ba.submit = function (e) { } // Okay, send the data to Balanced. - balanced.bankAccount.create( bankAccount - , Gratipay.payments.associate('balanced-ba') - ); + balanced.bankAccount.create(bankAccount, function (response) { + if (response.status_code !== 201) { + return Gratipay.payments.ba.onError(response); + } + + /* The request to tokenize the thing succeeded. Now we need to associate it + * to the Customer on Balanced and to the participant in our DB. + */ + Gratipay.payments.associate('balanced-ba', response.bank_accounts[0].href); + }); +}; + +Gratipay.payments.ba.onError = function(response) { + $('button#save').prop('disabled', false); + var msg = response.status_code + ": " + + $.map(response.errors, function(obj) { return obj.description }).join(', '); + Gratipay.notification(msg, 'error', -1); + return msg; }; @@ -137,7 +131,7 @@ Gratipay.payments.cc.init = function() { Gratipay.payments.init(); // Lazily depend on Braintree. - Gratipay.payments.lazyLoad("https://js.braintreegateway.com/v2/braintree.js") + Gratipay.payments.lazyLoad("https://js.braintreegateway.com/v2/braintree.js") $('form#credit-card').submit(Gratipay.payments.cc.submit); Gratipay.payments.cc.formatInputs( @@ -279,57 +273,33 @@ Gratipay.payments.cc.submit = function(e) { $('button#save').prop('disabled', true); Gratipay.forms.clearInvalid($(this)); - // Adapt our form lingo to balanced nomenclature. + // Adapt our form lingo to braintree nomenclature. function val(field) { return $('form#credit-card #'+field).val(); } - var credit_card = {}; // holds CC info + var credit_card = {}; credit_card.number = val('card_number').replace(/[^\d]/g, ''); credit_card.cvv = val('cvv'); - credit_card.name = val('name'); - var country = val('country') || null; - - credit_card.meta = { 'address_2': val('address_2') - , 'region': val('state') - , 'city_town': val('city_town') - , 'country': country - }; - - // XXX We're duping some of this info in both meta and address due to - // evolution of the Balanced API and our stepwise keeping-up. See: - // https://github.com/gratipay/gratipay.com/issues/2446 and links from - // there. - credit_card.address = { 'line1': val('address_1') - , 'line2': val('address_2') - , 'city': val('city_town') - , 'state': val('state') - , 'postal_code': val('zip') - , 'country_code': country - }; - - credit_card.expiration_month = val('expiration_month'); + credit_card.cardholderName = val('name'); + credit_card.billingAddress = { 'postalCode': val('zip') }; + credit_card.expirationMonth = val('expiration_month'); var year = val('expiration_year'); - credit_card.expiration_year = year.length == 2 ? '20' + year : year; + credit_card.expirationYear = year.length == 2 ? '20' + year : year; - var is_card_number_invalid = !balanced.card.isCardNumberValid(credit_card.number); - var is_expiry_invalid = !balanced.card.isExpiryValid(credit_card.expiration_month, - credit_card.expiration_year); - var is_cvv_invalid = !balanced.card.isSecurityCodeValid(credit_card.number, - credit_card.cvv); + // TODO: Client Side validation - Gratipay.forms.setInvalid($('#card_number'), is_card_number_invalid); - Gratipay.forms.setInvalid($('#expiration_month'), is_expiry_invalid); - Gratipay.forms.setInvalid($('#cvv'), is_cvv_invalid); + var client = new braintree.api.Client({clientToken: val('braintree_token')}); - if (is_card_number_invalid || is_expiry_invalid || is_cvv_invalid) { - $('button#save').prop('disabled', false); - Gratipay.forms.focusInvalid($(this)); - return false; - } + client.tokenizeCard(credit_card, function (err, nonce) { + if (err) { + Gratipay.notification(err, 'error') + } else { + Gratipay.payments.associate('braintree-cc', nonce); + } + }); - balanced.card.create(credit_card, Gratipay.payments.associate('balanced-cc')); return false; }; From 1d3be33cb4bb9781f42a9ee326623559a95d1291 Mon Sep 17 00:00:00 2001 From: Rohit Paul Kuruvilla Date: Sat, 9 May 2015 00:58:23 +0530 Subject: [PATCH 07/25] Implement invalidation of braintree cards --- gratipay/models/exchange_route.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/gratipay/models/exchange_route.py b/gratipay/models/exchange_route.py index 6689356a24..5cd55ca22e 100644 --- a/gratipay/models/exchange_route.py +++ b/gratipay/models/exchange_route.py @@ -1,6 +1,7 @@ from __future__ import absolute_import, division, print_function, unicode_literals import balanced +import braintree from postgres.orm import Model @@ -74,12 +75,11 @@ def insert(cls, participant, network, address, error='', fee_cap=None): return r def invalidate(self): - if self.network.startswith('balanced-'): - if self.network == 'balanced-cc': - balanced.Card.fetch(self.address).unstore() - else: - assert self.network == 'balanced-ba' - balanced.BankAccount.fetch(self.address).delete() + if self.network == 'balanced-ba': + balanced.BankAccount.fetch(self.address).delete() + elif self.network == 'braintree-cc': + braintree.PaymentMethod.delete(self.address) + self.update_error('invalidated') def update_error(self, new_error, propagate=True): From 59ca2f3d7539b9f6ef3e701c3a5754b27063c7ba Mon Sep 17 00:00:00 2001 From: Rohit Paul Kuruvilla Date: Sat, 9 May 2015 01:55:15 +0530 Subject: [PATCH 08/25] Use braintree_customer_id Initially, I was thinking of linking braintree's customer ID directly to the participant's ID in our database. Later decided against it, coz (a) We don't yet know if it'll be possible to map the custom ID when we migrate cards from Balanced to Braintree (b) We'll end up adding a DB column like `has_braintree_account` anyway for caching purposes, it's a lot easier to just reuse the logic that we had with balanced (especially in the tests) --- sql/branch.sql | 4 +++- www/%username/routes/associate.json.spt | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/sql/branch.sql b/sql/branch.sql index 3dde6b0388..1c5bc537dc 100644 --- a/sql/branch.sql +++ b/sql/branch.sql @@ -1 +1,3 @@ -ALTER TYPE payment_net ADD VALUE 'braintree-cc'; \ No newline at end of file +ALTER TYPE payment_net ADD VALUE 'braintree-cc'; + +ALTER TABLE participants ADD COLUMN braintree_customer_id text DEFAULT NULL; \ No newline at end of file diff --git a/www/%username/routes/associate.json.spt b/www/%username/routes/associate.json.spt index 9389723748..9ca4a9bce7 100644 --- a/www/%username/routes/associate.json.spt +++ b/www/%username/routes/associate.json.spt @@ -38,7 +38,7 @@ if change: elif network == 'braintree-cc': result = braintree.PaymentMethod.create({ - "customer_id": participant.id, + "customer_id": participant.braintree_customer_id, "payment_method_nonce": address, "options": {"verify_card": True} }) From 374b14127c01e9c19232040b70b103944bdaff2f Mon Sep 17 00:00:00 2001 From: Rohit Paul Kuruvilla Date: Sat, 9 May 2015 01:58:30 +0530 Subject: [PATCH 09/25] Add get_braintree_account and get_braintree_token --- gratipay/models/participant.py | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/gratipay/models/participant.py b/gratipay/models/participant.py index cfa555ac53..d2a903f858 100644 --- a/gratipay/models/participant.py +++ b/gratipay/models/participant.py @@ -19,6 +19,7 @@ from aspen.utils import utcnow import balanced +import braintree from dependency_injection import resolve_dependencies from markupsafe import escape as htmlescape from postgres.orm import Model @@ -774,7 +775,7 @@ def get_bank_account_error(self): return getattr(ExchangeRoute.from_network(self, 'balanced-ba'), 'error', None) def get_credit_card_error(self): - return getattr(ExchangeRoute.from_network(self, 'balanced-cc'), 'error', None) + return getattr(ExchangeRoute.from_network(self, 'braintree-cc'), 'error', None) def get_cryptocoin_addresses(self): routes = self.db.all(""" @@ -1364,6 +1365,33 @@ def get_balanced_account(self): customer = balanced.Customer.fetch(self.balanced_customer_href) return customer + def get_braintree_account(self): + """Fetch or create a braintree account for this participant. + """ + if not self.braintree_customer_id: + customer = braintree.Customer.create({ + 'custom_fields': {'participant_id': self.id} + }).customer + + r = self.db.one(""" + UPDATE participants + SET braintree_customer_id=%s + WHERE id=%s + AND braintree_customer_id IS NULL + RETURNING id + """, (customer.id, self.id)) + + if not r: + return self.get_braintree_account() + else: + customer = braintree.Customer.find(self.braintree_customer_id) + return customer + + def get_braintree_token(self): + account = self.get_braintree_account() + + token = braintree.ClientToken.generate({'customer_id': account.id}) + return token class StillReceivingTips(Exception): pass class BalanceIsNotZero(Exception): pass From 6cf3e74be6dad322dbfdc734b627fbc63376a387 Mon Sep 17 00:00:00 2001 From: Rohit Paul Kuruvilla Date: Sat, 9 May 2015 01:58:48 +0530 Subject: [PATCH 10/25] Add braintree token to credit card form --- www/%username/routes/credit-card.html.spt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/www/%username/routes/credit-card.html.spt b/www/%username/routes/credit-card.html.spt index ce94fddeed..c649038718 100644 --- a/www/%username/routes/credit-card.html.spt +++ b/www/%username/routes/credit-card.html.spt @@ -1,4 +1,5 @@ import balanced +import braintree from gratipay.models.exchange_route import ExchangeRoute from gratipay.utils import get_participant @@ -72,6 +73,8 @@ title = _("Credit Card")

+ +

{{ _("Required") }}

@@ -93,7 +94,7 @@ title = _("Credit Card") {{ _("This expiration date is invalid.") }} / + value="{{ (card.expiration_year|int) - 2000 if card.expiration_year }}" required />
+ +
{{ _("To minimize processing fees, we charge your credit card at least $10 at a time " "(anything extra stays in Gratipay to use in future weeks). {0}Read More{1}", @@ -113,43 +119,7 @@ title = _("Credit Card") - - - - - - - - - - - - From 4fca7e02d0ea36d9c912881c41f062ef02b0af67 Mon Sep 17 00:00:00 2001 From: Rohit Paul Kuruvilla Date: Sat, 9 May 2015 15:09:26 +0530 Subject: [PATCH 14/25] Backward compatibility for invalidation --- gratipay/models/exchange_route.py | 2 ++ www/%username/routes/credit-card.html.spt | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/gratipay/models/exchange_route.py b/gratipay/models/exchange_route.py index 5cd55ca22e..091ad573ee 100644 --- a/gratipay/models/exchange_route.py +++ b/gratipay/models/exchange_route.py @@ -77,6 +77,8 @@ def insert(cls, participant, network, address, error='', fee_cap=None): def invalidate(self): if self.network == 'balanced-ba': balanced.BankAccount.fetch(self.address).delete() + elif self.network == 'balanced-cc': + balanced.Card.fetch(self.address).unstore() elif self.network == 'braintree-cc': braintree.PaymentMethod.delete(self.address) diff --git a/www/%username/routes/credit-card.html.spt b/www/%username/routes/credit-card.html.spt index caeb7d237b..da745b2749 100644 --- a/www/%username/routes/credit-card.html.spt +++ b/www/%username/routes/credit-card.html.spt @@ -127,7 +127,7 @@ title = _("Credit Card") {% if status != "missing" %} -
From 39567d0920e2fbe199744ad015ac58ab2ccd3c52 Mon Sep 17 00:00:00 2001 From: Rohit Paul Kuruvilla Date: Sat, 9 May 2015 16:59:41 +0530 Subject: [PATCH 15/25] Fix credit card page for Balanced cards --- gratipay/billing/instruments.py | 36 +++++++++++++++++++++++ gratipay/billing/null_objects.py | 15 ---------- tests/py/test_routes.py | 4 +++ www/%username/routes/credit-card.html.spt | 18 +++++++----- 4 files changed, 51 insertions(+), 22 deletions(-) create mode 100644 gratipay/billing/instruments.py delete mode 100644 gratipay/billing/null_objects.py diff --git a/gratipay/billing/instruments.py b/gratipay/billing/instruments.py new file mode 100644 index 0000000000..2f1d74846e --- /dev/null +++ b/gratipay/billing/instruments.py @@ -0,0 +1,36 @@ +import braintree +import balanced + +class CreditCard: + def __init__(self, *args, **kwargs): + fields = [ + 'number', + 'expiration_year', + 'expiration_month', + 'address_postal_code', + 'cardholder_name' + ] + for field in fields: + setattr(self, field, kwargs.pop(field, '')) + + @classmethod + def from_route(cls, route): + if route.network == 'braintree-cc': + card = braintree.PaymentMethod.find(route.address) + return cls( + number=card.masked_number, + expiration_month=card.expiration_month, + expiration_year=card.expiration_year, + address_postal_code=card.billing_address.postal_code, + cardholder_name=card.cardholder_name + ) + else: + assert route.network == 'balanced-cc' + card = balanced.Card.fetch(route.address) + return cls( + number=card.number, + expiration_month=card.expiration_month, + expiration_year=card.expiration_year, + address_postal_code=card.address['postal_code'], + cardholder_name=card.name + ) diff --git a/gratipay/billing/null_objects.py b/gratipay/billing/null_objects.py deleted file mode 100644 index 3df70d8284..0000000000 --- a/gratipay/billing/null_objects.py +++ /dev/null @@ -1,15 +0,0 @@ -class NullCard: - def __init__(self, *args, **kwargs): - return None - - def __call__(self, *args, **kwargs): - return self - - def __getattr__(self, mname): - return self - - def __nonzero__(self): - return False - - def __unicode__(self): - return u'' diff --git a/tests/py/test_routes.py b/tests/py/test_routes.py index 160cdeb37c..2de83d42e7 100644 --- a/tests/py/test_routes.py +++ b/tests/py/test_routes.py @@ -116,6 +116,10 @@ def test_credit_card_page_loads_when_there_is_a_card(self): actual = self.client.GET('/janet/routes/credit-card.html', auth_as='janet').body.decode('utf8') assert expected in actual + def test_credit_card_page_shows_details_for_balanced_cards(self): + response = self.client.GET('/janet/routes/credit-card.html', auth_as='janet').body.decode('utf8') + assert self.card.number in response + def test_credit_card_page_shows_card_failing(self): ExchangeRoute.from_network(self.janet, 'balanced-cc').update_error('Some error') expected = 'Your credit card is failing' diff --git a/www/%username/routes/credit-card.html.spt b/www/%username/routes/credit-card.html.spt index da745b2749..dbc1be9584 100644 --- a/www/%username/routes/credit-card.html.spt +++ b/www/%username/routes/credit-card.html.spt @@ -3,7 +3,7 @@ import braintree from gratipay.models.exchange_route import ExchangeRoute from gratipay.utils import get_participant -from gratipay.billing.null_objects import NullCard +from gratipay.billing.instruments import CreditCard [-----------------------------------------------------------------------------] @@ -15,9 +15,13 @@ if not user.ANON: status = "missing" status_msg = _("Your credit card is {0}missing{1}") - route = ExchangeRoute.from_network(participant, 'braintree-cc') + braintree_route = ExchangeRoute.from_network(participant, 'braintree-cc') + balanced_route = ExchangeRoute.from_network(participant, 'balanced-cc') + + route = braintree_route or balanced_route + if route: - card = braintree.PaymentMethod.find(route.address) + card = CreditCard.from_route(route) last_bill_result = route.error if last_bill_result == "": status = "working" @@ -26,7 +30,7 @@ if not user.ANON: status = "failing" status_msg = _("Your credit card is {0}failing{1}") else: - card = NullCard() + card = CreditCard() title = _("Credit Card") @@ -82,8 +86,8 @@ title = _("Credit Card") {{ _("Credit Card Number") }} {{ _("This card number is invalid.") }} - {% if card.masked_number %}{{ - _("Current: {0}", card.masked_number) + {% if card.number %}{{ + _("Current: {0}", card.number) }}{% endif %} @@ -105,7 +109,7 @@ title = _("Credit Card")
From f0c2950e6a93fdea713d970825790d5eca288d96 Mon Sep 17 00:00:00 2001 From: Rohit Paul Kuruvilla Date: Sat, 9 May 2015 17:00:07 +0530 Subject: [PATCH 16/25] Update test fixtures --- tests/py/fixtures/TestRoutes.yml | 637 +++++++++++++++++-------------- 1 file changed, 349 insertions(+), 288 deletions(-) diff --git a/tests/py/fixtures/TestRoutes.yml b/tests/py/fixtures/TestRoutes.yml index 3f76a463e9..bdf94c353d 100644 --- a/tests/py/fixtures/TestRoutes.yml +++ b/tests/py/fixtures/TestRoutes.yml @@ -6,24 +6,25 @@ interactions: method: POST uri: https://api.balancedpayments.com:443/bank_accounts response: - body: {string: !!python/unicode "{\n \"bank_accounts\": [\n {\n \"routing_number\": - \"321174851\",\n \"bank_name\": \"SAN MATEO CREDIT UNION\",\n \"account_type\": - \"checking\",\n \"name\": \"Alice G. Krebs\",\n \"links\": {\n \"customer\": - null,\n \"bank_account_verification\": null\n },\n \"can_credit\": - true,\n \"created_at\": \"2015-03-23T16:19:20.373041Z\",\n \"fingerprint\": - \"5f0ba9fa3f1122ef13b944a40abfe44e7eba9e16934e64200913cb4c402ace14\",\n \"updated_at\": - \"2015-03-23T16:19:20.373042Z\",\n \"href\": \"/bank_accounts/BA5aSjvNl8sltPNNvv9GBM0E\",\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\": \"BA5aSjvNl8sltPNNvv9GBM0E\"\n - \ }\n ],\n \"links\": {\n \"bank_accounts.settlements\": \"/bank_accounts/{bank_accounts.id}/settlements\",\n - \ \"bank_accounts.bank_account_verification\": \"/verifications/{bank_accounts.bank_account_verification}\",\n - \ \"bank_accounts.debits\": \"/bank_accounts/{bank_accounts.id}/debits\",\n - \ \"bank_accounts.customer\": \"/customers/{bank_accounts.customer}\",\n - \ \"bank_accounts.credits\": \"/bank_accounts/{bank_accounts.id}/credits\",\n - \ \"bank_accounts.bank_account_verifications\": \"/bank_accounts/{bank_accounts.id}/verifications\"\n - \ }\n}"} + body: {string: !!python/unicode "{\n \"bank_accounts\": [\n {\n \"routing_number\"\ + : \"321174851\",\n \"bank_name\": \"SAN MATEO CREDIT UNION\",\n \ + \ \"account_type\": \"checking\",\n \"name\": \"Alice G. Krebs\",\n \ + \ \"links\": {\n \"customer\": null,\n \"bank_account_verification\"\ + : null\n },\n \"can_credit\": true,\n \"created_at\": \"2015-05-09T11:27:28.996649Z\"\ + ,\n \"fingerprint\": \"5f0ba9fa3f1122ef13b944a40abfe44e7eba9e16934e64200913cb4c402ace14\"\ + ,\n \"updated_at\": \"2015-05-09T11:27:28.996651Z\",\n \"href\"\ + : \"/bank_accounts/BA3zTiYyqLuXS7YWAi9IaC7H\",\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\": \"BA3zTiYyqLuXS7YWAi9IaC7H\"\ + \n }\n ],\n \"links\": {\n \"bank_accounts.settlements\": \"/bank_accounts/{bank_accounts.id}/settlements\"\ + ,\n \"bank_accounts.bank_account_verification\": \"/verifications/{bank_accounts.bank_account_verification}\"\ + ,\n \"bank_accounts.debits\": \"/bank_accounts/{bank_accounts.id}/debits\"\ + ,\n \"bank_accounts.customer\": \"/customers/{bank_accounts.customer}\"\ + ,\n \"bank_accounts.credits\": \"/bank_accounts/{bank_accounts.id}/credits\"\ + ,\n \"bank_accounts.bank_account_verifications\": \"/bank_accounts/{bank_accounts.id}/verifications\"\ + \n }\n}"} headers: access-control-allow-headers: [Content-Type] access-control-allow-methods: ['POST, OPTIONS'] @@ -57,50 +58,50 @@ interactions: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/bank_accounts/BA5aSjvNl8sltPNNvv9GBM0E + uri: https://api.balancedpayments.com:443/bank_accounts/BA3zTiYyqLuXS7YWAi9IaC7H response: body: string: !!binary | - H4sIAAAAAAAAA41TTY+bMBC976+wOHeDbZxkyS3bRqtVtWzVTS+tKmTM0HXDR2RM1Cjiv68hUD5C - pHCwhN/M+M28N6c7hKyApzufC5EVqc6tFfplLhE61aeBVVZomf7x0yIJQBnccighS/YwJ9anNqiu - kfIEKvxt7aGX9Xbzij5/33x53qIf3vOr1wU3b/n6uK/jxTuInXmii2grrWMpAD3N0FcFQd7hsUx3 - FdWWpKEpilxnSU0wLeK4ZTbqzz+AkpEUXMssbUKbHsr/zQie+kJBKLWJ0KqADlHANYQ+rxCLYjK/ - x849dbZksSLuiuKZs3QwIz87qpFpDNReybTOmUc44G7EnYgQSiEiTuAyxhnmQQSMwRIMDGThOgwW - jGLsEkcETDBMuQDCusLFPryFC+1xeVcQVSTsgeT243rO3/4evPghj/U3zzsc3KfHF7zp3kpA82rc - 3YxaDTtX/Ks/jHHPFjwMFeRjpaQ+XqpkJAU6fU0ur3Nter+83mcGiH2RhRNgbXB17KOT0ocQ1MpH - PM476WVYTe7qqOpKpTl/V2axhgYdrtgsB61jSOC8bmM1TgNxZjIs7X7C2Yujiv2UscUtu+/53B7V - v5paNvKPnqqnUyl6A+8mdpJyb18tu/25YNcCV8ict/RGNm3wJJ2rY7ix+GDGlrFAeVd+ABIaYANe - BQAA + H4sIAAAAAAAAA41T246bMBB936+weG6DDU6y5I1uV23UNit1U7XbqkLGDF0rXFJjqk0j/r3GgXIJ + kYIQEnPGM2fmHB9vELJClu0CxnleZqqwVuiHDiJ0NF8Ny7xUIvsVZGUagtS45TqELOntnFiv2iRT + I2Mp1Pijv0Gf/O39A7r7fP92vUVfNuuHTZfc9ArUYW/y+TPwnW7RZbSV/ERwQO9m6IOEsOjwRGS7 + mmpLUtPkZaHy1BDMyiRpmY3mC/6AFLHgTIk8a1KbGar/w3CWBVxCJJTOULKEDpHAFEQBqxHLwWT+ + GuvX2xKycpYr53bmeYsF9b53VGM9GMi9FJk5M49xyLyYuTEhjgMxcUOPUkYxC2OgFJagYSALz6Ww + oA7GHnF5SDnFDuNAaFe43EdXcJmTHpdnCXFNwh5Ibr/x3b9b8XT4/bH89rh8+uoLb83ulu+7Xiko + Vq+721GrYeeKF/NgjHu2YFEkoRgrJdThXCUtKTjTYXIeLpSe/Ty8zzWQBDyPJkBjcHnoo5PSRxAa + 5WOWFJ30Iqo3d3FVplKlvz9rs1hDgw6v2KwApRJI4XTdxmocB+LMRFTZ/QMnL44q9o+MLW7Zfc8X + 9qj+xaNVI/+oldlOregVvJvcScq9+2rZ7c8Zuxa4QOZ0S69k0yZP0rm4hiuLD3ZsaQtUN9U/cRed + OF4FAAA= headers: content-encoding: [gzip] - content-length: ['516'] + content-length: ['518'] content-type: [application/json] status: {code: 200, message: OK} - request: body: '{"customer": null, "bank_name": "SAN MATEO CREDIT UNION", "account_type": "checking", "name": "Alice G. Krebs", "links": {"customer": "/customers/CU6gH6X342hhVXOrrH7SVFzL"}, - "can_credit": true, "created_at": "2015-03-23T16:19:20.373041Z", "address": + "can_credit": true, "created_at": "2015-05-09T11:27:28.996649Z", "address": {"city": null, "line2": null, "line1": null, "state": null, "postal_code": null, - "country_code": null}, "updated_at": "2015-03-23T16:19:20.373042Z", "meta": + "country_code": null}, "updated_at": "2015-05-09T11:27:28.996651Z", "meta": {}, "bank_account_verification": null, "fingerprint": "5f0ba9fa3f1122ef13b944a40abfe44e7eba9e16934e64200913cb4c402ace14", - "routing_number": "321174851", "can_debit": false, "id": "BA5aSjvNl8sltPNNvv9GBM0E", + "routing_number": "321174851", "can_debit": false, "id": "BA3zTiYyqLuXS7YWAi9IaC7H", "account_number": "xxxxxx0001"}' headers: {} method: PUT - uri: https://api.balancedpayments.com:443/bank_accounts/BA5aSjvNl8sltPNNvv9GBM0E + uri: https://api.balancedpayments.com:443/bank_accounts/BA3zTiYyqLuXS7YWAi9IaC7H response: body: string: !!binary | - H4sIAAAAAAAAA41TXW+bMBR9769APK9gg0NK3tIua6utZFrSqto0IWMujRc+ImOiZhH/fYaEQkgy - hQdL+Fwfn3vO9fZK0/SApkufMpYVqcz1kfZLbWratl4VLLJC8vTNT4skAKFw3bYwHpKbAdY/NUU1 - R0oTqPDZ2NOexvPJVLv7Mfn8ONeevcep1xbv7/LlZlXXswWwpbqirWiYxjFnoN0b2lcBQd7iMU+X - ldRGpJLJilxmyU7g3bPz9uC82sRaLF5ep0I8DGcvX/5++yDode2vQfCIMyp5liratIjjfWflR4uM - pj4TEHKpKqQooEUEUAmhTytEtxAeXCP72rLn2Blhd2Qhwx7aiOCfbQORahfESvC0PjOIUEDdiNoR - xpYFEbYDlxBKEA0iIASGoGDAjmsTcIiFkIttFhBGkEUZYNISF6vwv1qw4SDHddyOloWAqBJhHgyC - eTse0NmftRff5LH87nnrtXt/+4Qm7V0JSFqF0HrUJNvOynv9IYQ6w0LDUEDez4/Lzd76xliVkgoa - rNPb+Hg7l6r34+1VpoDYZ1l4AqzHXmy66MnoQwjq5CMa5230PKycO2tVzVSq9XfVU9VNZ2wPH56R - g5QxJLB7hP00tgfhGDwsze6BnWU9xu6R/ojrZnfmc7PHf/ZouY+/d1XtTpXoBbr3tScld1+x2fwc - qWuAM2J2r/RCNU3xSTlnbbiQ/MBjXY1AeVX+A3eHzjB0BQAA + H4sIAAAAAAAAA41T0Y6aQBR9368gPLcyAyOIb9Zuq+lWk+pu3TYNGYbLOhHBDkNT1/DvHVAWRGwk + hIQ5d86ce86dw52m6T6NNx5lLMlimepD7ada1LRD+VWwSDLJ4xcvzrY+CIXrlomxQwZ9rL+rikqO + mG6hwBejmfZ1tLyfa+Nv9x+nS+1xNp3P6uLTWZ7c78p6tga2UUfUFRXTKOIMtM897YsAP63xiMeb + QmolUslkWSqT7VHg+NF+mdgri5jr9dNqLsTEWTx9en14I2h17f0BwUPOqORJrGjjLIpOneVvLTIa + e0xAwKWqkCKDGhFAJQQeLRDdRLj/HqnXXWI8NJ2hOei5rm0T90fdQKjaBbETPC739EPkUzekVoix + aUKILd8lhBJE/RAIAQcUDNh2LQI2MRFyscV8wggyKQNMauJsF/xPi4V6A8fu962GlrWAsBBhnA2C + 8WFkvS758/73Q7ZaOM/fR9yd0rEzqc/agqRFCLVHVbL1rPwtH4RQY1hoEAhI2/lxuT9ZXxmrUlJB + g9m9jC+XU6l6v1zeJQqIPJYEHWA59mLfRDujD8Avkw9plNbR86Bw7qpVJVOuvr+KnopuGmN7fvF6 + KUgZwRaOl7CdxuEsnB4PcqO54WhZi7G5pT3iutGc+dRo8V/dmp/ibx1VulMkeoPuU22n5OYtNqqf + C3UVcEXM8ZbeqKYq7pRz1YYbyc881tUI5Hf5P99b4KF0BQAA headers: content-encoding: [gzip] content-length: ['549'] @@ -114,18 +115,18 @@ interactions: response: body: string: !!binary | - H4sIAAAAAAAAA61UTW/aQBC951dYPvTU4F17MTFSVJGUJlEbUjUfilpV1no9DtsYG63XKDTiv3d3 - sbFNoOIQDkjMzM68ee8xr0eWZUc0ew4pY3mZycIeWr9U0LJezbdKi7yUPHsKs3IWgVB523MxHpCT - PrY/1kWmR0ZnoPO3o4l1Pbob31jnP8afr+6s+8nVzaQprmaFcjk39WwK7FmNaCrqTqOUM7AuetZX - AVHR5FOePWuoNUgFk5WFzGdrgOf3/tOl/+gRdzp9eLwR4nJw+/Dl77dNg62twwUInnBGJc8z1TYr - 07TabLVZkdEsZAJiLlWFFCU0GQFUQhxSnbFdhPvHyDt2vTvsD3EwdFHPG3iI4J/NAolaF8Rc8My8 - 6ScookFCvQRj14UEe1FACCWIRgkQAgNQacB+4BHwiYtQgD0WEUaQSxlg0jQu5/F/seCej/zAD1pY - pgISDcLpGME5G/Xp7Z/FJD0pUvl9MlksgouzazRuZs1AUi1Cw1GtbOOVF/NBCLXMQuNYQLGtH5fL - ivqaWKWSEhrc3WH8NlxItfvb8DxXiTRkebwjaWwvlu3sTuljiIzyCU2LRnoea+b2UmU6rdT3b72T - XRNmwnZKCyO+U1u3cPYZtyvNp5TPuDzF6EOeJAXIU1RpYmfwoltq/65JtDfivt8QM11NwaiasUah - InVgLmDB81Ir3IIic6WCflc9S7h4PwIUpcaG2jGt09A9bj1FlkxhButDt+34184foMfjldN+UMHu - FrV/bZ8R22nflcLZ6r/36aqWsztqfXw0qQcAr4t3gm7fypb7tvDVVXvgmD/EgWiq2p1g9tJwYO8O - x7b2wdHqHyiwehTYBgAA + H4sIAAAAAAAAA61UXW+bMBR9769APOxpCzY4ECpVU9Z1a7Uukda0azdNyJhLY5VAZkzVtMp/n+1A + gTSZ+tAoQuJ+nnvO5T4dWJYd0/wuoowVVS5L+9D6rYyW9WSeyi2KSvL8NsqrRQxC+W3PxTggoyG2 + 3zdBpkZOF6D9F+OJ9X08O5laxz9OPp/NrMvJ2XTSBte9Irlamng2B3anWrQRTaVxxhlYXwfWNwFx + 2foznt9pqA1IBZNVpSwWG4DHl/7tqX/tEXc+v7qeCnEaXFx9eTx/LrA1dXQPgqecUcmLXJXNqyyr + J1s/j8hoHjEBCZcqQooKWo8AKiGJqPbYLsLDD0j9wxnGh25w6I4GYej7JPzVDpCqcUEsBc9NzjBF + MQ1T6qUYuy6k2ItDQihBNE6BEAhAuQH7oUfAJy5CIfZYTBhBLmWASVu4Wib/w+KhwSjwh0Ovg2Uu + INUgnN4iOJ/G3uOM36z+nlfXF8HNzzEPz+hxcNr2WoCkWoSWo0bZdlcezA8h1FkWmiQCym39uFzV + 1DfEKpWU0ODuNuOX5lKq2V+al4VyZBErkh1Os/Zi1fXulD6B2Cif0qxspeeJZm4vVabSWj3/6Jns + hjBjtjNaGvGdZnVLZ9/i9qX5mPEFl0cYvSvStAR5hGpN7BwedEm9vxsS7Wdx366J6a66YFT32KBQ + lsawFHDPi0or3IEiC6WCzqvTUi7ejgBFqVlDvTGd09A/bgNFlsxgAZtDt73xT70PYMCTtdNNqGH3 + g7pv22fEdrp3pXS26u9NXTdy9lttjo8m9RXAm+CdoLu3srN9W/iaqD1wzAfxSjR17E4we2l4Ze0e + x7beg4P1P2cTce3YBgAA headers: content-encoding: [gzip] content-length: ['642'] @@ -139,18 +140,18 @@ interactions: response: body: string: !!binary | - H4sIAAAAAAAAA61UTW/aQBC951dYPvTU4F17MTFSVJGUJlEbUjUfilpV1no9DtsYG63XKDTiv3d3 - sbFNoOIQDkjMzM68ee8xr0eWZUc0ew4pY3mZycIeWr9U0LJezbdKi7yUPHsKs3IWgVB523MxHpCT - PrY/1kWmR0ZnoPO3o4l1Pbob31jnP8afr+6s+8nVzaQprmaFcjk39WwK7FmNaCrqTqOUM7AuetZX - AVHR5FOePWuoNUgFk5WFzGdrgOf3/tOl/+gRdzp9eLwR4nJw+/Dl77dNg62twwUInnBGJc8z1TYr - 07TabLVZkdEsZAJiLlWFFCU0GQFUQhxSnbFdhPvHyDt2vTvsD3EwdFHPG3iI4J/NAolaF8Rc8My8 - 6ScookFCvQRj14UEe1FACCWIRgkQAgNQacB+4BHwiYtQgD0WEUaQSxlg0jQu5/F/seCej/zAD1pY - pgISDcLpGME5G/Xp7Z/FJD0pUvl9MlksgouzazRuZs1AUi1Cw1GtbOOVF/NBCLXMQuNYQLGtH5fL - ivqaWKWSEhrc3WH8NlxItfvb8DxXiTRkebwjaWwvlu3sTuljiIzyCU2LRnoea+b2UmU6rdT3b72T - XRNmwnZKCyO+U1u3cPYZtyvNp5TPuDzF6EOeJAXIU1RpYmfwoltq/65JtDfivt8QM11NwaiasUah - InVgLmDB81Ir3IIic6WCflc9S7h4PwIUpcaG2jGt09A9bj1FlkxhButDt+34184foMfjldN+UMHu - FrV/bZ8R22nflcLZ6r/36aqWsztqfXw0qQcAr4t3gm7fypb7tvDVVXvgmD/EgWiq2p1g9tJwYO8O - x7b2wdHqHyiwehTYBgAA + H4sIAAAAAAAAA61UXW+bMBR9769APOxpCzY4ECpVU9Z1a7Uukda0azdNyJhLY5VAZkzVtMp/n+1A + gTSZ+tAoQuJ+nnvO5T4dWJYd0/wuoowVVS5L+9D6rYyW9WSeyi2KSvL8NsqrRQxC+W3PxTggoyG2 + 3zdBpkZOF6D9F+OJ9X08O5laxz9OPp/NrMvJ2XTSBte9Irlamng2B3anWrQRTaVxxhlYXwfWNwFx + 2foznt9pqA1IBZNVpSwWG4DHl/7tqX/tEXc+v7qeCnEaXFx9eTx/LrA1dXQPgqecUcmLXJXNqyyr + J1s/j8hoHjEBCZcqQooKWo8AKiGJqPbYLsLDD0j9wxnGh25w6I4GYej7JPzVDpCqcUEsBc9NzjBF + MQ1T6qUYuy6k2ItDQihBNE6BEAhAuQH7oUfAJy5CIfZYTBhBLmWASVu4Wib/w+KhwSjwh0Ovg2Uu + INUgnN4iOJ/G3uOM36z+nlfXF8HNzzEPz+hxcNr2WoCkWoSWo0bZdlcezA8h1FkWmiQCym39uFzV + 1DfEKpWU0ODuNuOX5lKq2V+al4VyZBErkh1Os/Zi1fXulD6B2Cif0qxspeeJZm4vVabSWj3/6Jns + hjBjtjNaGvGdZnVLZ9/i9qX5mPEFl0cYvSvStAR5hGpN7BwedEm9vxsS7Wdx366J6a66YFT32KBQ + lsawFHDPi0or3IEiC6WCzqvTUi7ejgBFqVlDvTGd09A/bgNFlsxgAZtDt73xT70PYMCTtdNNqGH3 + g7pv22fEdrp3pXS26u9NXTdy9lttjo8m9RXAm+CdoLu3srN9W/iaqD1wzAfxSjR17E4we2l4Ze0e + x7beg4P1P2cTce3YBgAA headers: content-encoding: [gzip] content-length: ['642'] @@ -160,20 +161,20 @@ interactions: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/bank_accounts/BA5aSjvNl8sltPNNvv9GBM0E + uri: https://api.balancedpayments.com:443/bank_accounts/BA3zTiYyqLuXS7YWAi9IaC7H response: body: string: !!binary | - H4sIAAAAAAAAA41TXW+bMBR9769APK9gg0NK3tIua6utZFrSqto0IWMujRc+ImOiZhH/fYaEQkgy - hQdL+Fwfn3vO9fZK0/SApkufMpYVqcz1kfZLbWratl4VLLJC8vTNT4skAKFw3bYwHpKbAdY/NUU1 - R0oTqPDZ2NOexvPJVLv7Mfn8ONeevcep1xbv7/LlZlXXswWwpbqirWiYxjFnoN0b2lcBQd7iMU+X - ldRGpJLJilxmyU7g3bPz9uC82sRaLF5ep0I8DGcvX/5++yDode2vQfCIMyp5liratIjjfWflR4uM - pj4TEHKpKqQooEUEUAmhTytEtxAeXCP72rLn2Blhd2Qhwx7aiOCfbQORahfESvC0PjOIUEDdiNoR - xpYFEbYDlxBKEA0iIASGoGDAjmsTcIiFkIttFhBGkEUZYNISF6vwv1qw4SDHddyOloWAqBJhHgyC - eTse0NmftRff5LH87nnrtXt/+4Qm7V0JSFqF0HrUJNvOynv9IYQ6w0LDUEDez4/Lzd76xliVkgoa - rNPb+Hg7l6r34+1VpoDYZ1l4AqzHXmy66MnoQwjq5CMa5230PKycO2tVzVSq9XfVU9VNZ2wPH56R - g5QxJLB7hP00tgfhGDwsze6BnWU9xu6R/ojrZnfmc7PHf/ZouY+/d1XtTpXoBbr3tScld1+x2fwc - qWuAM2J2r/RCNU3xSTlnbbiQ/MBjXY1AeVX+A3eHzjB0BQAA + H4sIAAAAAAAAA41T0Y6aQBR9368gPLcyAyOIb9Zuq+lWk+pu3TYNGYbLOhHBDkNT1/DvHVAWRGwk + hIQ5d86ce86dw52m6T6NNx5lLMlimepD7ada1LRD+VWwSDLJ4xcvzrY+CIXrlomxQwZ9rL+rikqO + mG6hwBejmfZ1tLyfa+Nv9x+nS+1xNp3P6uLTWZ7c78p6tga2UUfUFRXTKOIMtM897YsAP63xiMeb + QmolUslkWSqT7VHg+NF+mdgri5jr9dNqLsTEWTx9en14I2h17f0BwUPOqORJrGjjLIpOneVvLTIa + e0xAwKWqkCKDGhFAJQQeLRDdRLj/HqnXXWI8NJ2hOei5rm0T90fdQKjaBbETPC739EPkUzekVoix + aUKILd8lhBJE/RAIAQcUDNh2LQI2MRFyscV8wggyKQNMauJsF/xPi4V6A8fu962GlrWAsBBhnA2C + 8WFkvS758/73Q7ZaOM/fR9yd0rEzqc/agqRFCLVHVbL1rPwtH4RQY1hoEAhI2/lxuT9ZXxmrUlJB + g9m9jC+XU6l6v1zeJQqIPJYEHWA59mLfRDujD8Avkw9plNbR86Bw7qpVJVOuvr+KnopuGmN7fvF6 + KUgZwRaOl7CdxuEsnB4PcqO54WhZi7G5pT3iutGc+dRo8V/dmp/ibx1VulMkeoPuU22n5OYtNqqf + C3UVcEXM8ZbeqKYq7pRz1YYbyc881tUI5Hf5P99b4KF0BQAA headers: content-encoding: [gzip] content-length: ['549'] @@ -183,7 +184,7 @@ interactions: body: null headers: {} method: DELETE - uri: https://api.balancedpayments.com:443/bank_accounts/BA5aSjvNl8sltPNNvv9GBM0E + uri: https://api.balancedpayments.com:443/bank_accounts/BA3zTiYyqLuXS7YWAi9IaC7H response: body: {string: !!python/unicode ''} headers: @@ -197,24 +198,25 @@ interactions: method: POST uri: https://api.balancedpayments.com:443/cards response: - body: {string: !!python/unicode "{\n \"cards\": [\n {\n \"links\": {\n - \ \"customer\": null\n },\n \"fingerprint\": \"1e2c425a579a1c7f27a037f0662b855035ceadbff18a0adbe785fb07e2afe1cc\",\n - \ \"cvv_result\": null,\n \"number\": \"xxxxxxxxxxxx4242\",\n \"avs_postal_match\": - null,\n \"expiration_month\": 12,\n \"meta\": {},\n \"id\": - \"CC5f7iWotJIWnjSrXPdO9KGA\",\n \"category\": \"other\",\n \"type\": - \"credit\",\n \"cvv_match\": null,\n \"bank_name\": \"\",\n \"avs_street_match\": - null,\n \"brand\": \"Visa\",\n \"updated_at\": \"2015-03-23T16:19:24.144416Z\",\n - \ \"address\": {\n \"city\": null,\n \"line2\": null,\n - \ \"line1\": null,\n \"state\": null,\n \"postal_code\": - null,\n \"country_code\": null\n },\n \"can_debit\": true,\n - \ \"name\": null,\n \"expiration_year\": 2020,\n \"cvv\": null,\n - \ \"is_verified\": true,\n \"avs_result\": \"Postal code matches, - but street address not verified.\",\n \"can_credit\": false,\n \"href\": - \"/cards/CC5f7iWotJIWnjSrXPdO9KGA\",\n \"created_at\": \"2015-03-23T16:19:24.144414Z\"\n - \ }\n ],\n \"links\": {\n \"cards.card_holds\": \"/cards/{cards.id}/card_holds\",\n - \ \"cards.customer\": \"/customers/{cards.customer}\",\n \"cards.disputes\": - \"/cards/{cards.id}/disputes\",\n \"cards.debits\": \"/cards/{cards.id}/debits\"\n - \ }\n}"} + body: {string: !!python/unicode "{\n \"cards\": [\n {\n \"links\": {\n\ + \ \"customer\": null\n },\n \"fingerprint\": \"1e2c425a579a1c7f27a037f0662b855035ceadbff18a0adbe785fb07e2afe1cc\"\ + ,\n \"cvv_result\": null,\n \"number\": \"xxxxxxxxxxxx4242\",\n\ + \ \"avs_postal_match\": null,\n \"expiration_month\": 12,\n \ + \ \"meta\": {},\n \"id\": \"CC3FBS9UsQCrXGdjLRGKziko\",\n \"category\"\ + : \"other\",\n \"type\": \"credit\",\n \"cvv_match\": null,\n \ + \ \"bank_name\": \"\",\n \"avs_street_match\": null,\n \"brand\"\ + : \"Visa\",\n \"updated_at\": \"2015-05-09T11:27:34.082755Z\",\n \ + \ \"address\": {\n \"city\": null,\n \"line2\": null,\n \ + \ \"line1\": null,\n \"state\": null,\n \"postal_code\"\ + : null,\n \"country_code\": null\n },\n \"can_debit\": true,\n\ + \ \"name\": null,\n \"expiration_year\": 2020,\n \"cvv\": null,\n\ + \ \"is_verified\": true,\n \"avs_result\": \"Postal code matches,\ + \ but street address not verified.\",\n \"can_credit\": false,\n \ + \ \"href\": \"/cards/CC3FBS9UsQCrXGdjLRGKziko\",\n \"created_at\": \"\ + 2015-05-09T11:27:34.082754Z\"\n }\n ],\n \"links\": {\n \"cards.card_holds\"\ + : \"/cards/{cards.id}/card_holds\",\n \"cards.customer\": \"/customers/{cards.customer}\"\ + ,\n \"cards.disputes\": \"/cards/{cards.id}/disputes\",\n \"cards.debits\"\ + : \"/cards/{cards.id}/debits\"\n }\n}"} headers: access-control-allow-headers: [Content-Type] access-control-allow-methods: ['POST, OPTIONS'] @@ -248,221 +250,246 @@ interactions: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU6gH6X342hhVXOrrH7SVFzL + uri: https://api.balancedpayments.com:443/bank_accounts/BA123123123 response: - body: - string: !!binary | - H4sIAAAAAAAAA41Uy27bMBC85ysEnWvrYSFqfA1Q5FCgh7ZB0KIQaJKNiEiky0dR19C/d2lLMk2q - YS467M4MubMjHm+SJMVGadFTqdJt8h0KSXI8faHFUU+hyk3XvZtqHeMvFjqBAKaEkdgHQp1QpRlH - mgk+qowiw6xm9gRpShqkAZGWeVGtinxV1l/yu23+fltW67yu6yr/ls4ULOmrlM36riyKYuNQiNg1 - veC69YdhxB57//X2+eH2aVOVbfv49EnKh/rz44e/Hy9n7lvB/QHTVtKflp7NDmZxJTAat4jrRmmk - jTUy5WLVI43by3E91cizmP6y0DwNHLSzHSiS/miIEEmVtynM9MEDwp5gpbRcLhdh2V7c9wJE9gIa - XYMFWWhiYbiWB7cbDLIzCu6hVLOUOqV40yGlK39M2iPWBUV2FbgBzvph82MndcJ7if56jnCagWun - PKvsOC927A/jihwiRpI0rejIaZVOFBwyI0Pm4M5JdjR2iL80CJ9MishcQwMlQncsJjFiFrju3/of - H5xfesEM+kdTySEFbxsmhAeXsrZFLDlDAiZham80jZBnVMDXEnGFsH28IhpXyEAHHgnDYzNMoAX2 - b3iZURe5gqQTLFAQkpzf9lfCOWICLjy1JJqnCRSw3xaCGQX/53Az/APeQA0dkwYAAA== + body: {string: !!python/unicode "{\n \"errors\": [\n {\n \"status\":\ + \ \"Not Found\",\n \"category_code\": \"not-found\",\n \"description\"\ + : \"

The requested URL was not found on the server.

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

Your request\ + \ id is OHM79c92514f63d11e4a95a06429171ffad.\",\n \"status_code\": 404,\n\ + \ \"category_type\": \"request\",\n \"request_id\": \"OHM79c92514f63d11e4a95a06429171ffad\"\ + \n }\n ]\n}"} headers: - content-encoding: [gzip] - content-length: ['505'] + content-length: ['430'] content-type: [application/json] - status: {code: 200, message: OK} + status: {code: 404, message: NOT FOUND} - request: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/cards/CC5f7iWotJIWnjSrXPdO9KGA + uri: https://api.balancedpayments.com:443/cards/CC6j2lN0CEuf3wt77J2bQW7r response: body: string: !!binary | - H4sIAAAAAAAAA41Uy27bMBC85ysInRNboiXL9q3IoWh7aIAWTZGiEChyFbORSIGkjBiG/r0k9bTj - APFBsHa5O7M7I55uEAooUUwHO/THviB08k8bLrl4ceEh4E422sgKlI2Kpiz7k+3tUFJw8QyqVlwY - eySIANMYJyRJtySiaYFTEq7SIlyvcb5JknCVUCAsL4poQ0L7B9JNUuRhCpgUEFEajI3p4ZAp0E3p - +jroMSOaKveEgtfZL8YxnqrJQWe11IaUWUUM3V/2gNeaK2K4FFklhXH5CI8IFRji1jCNyZnDu79P - ipQ/SvP1y6P490P9fmDft98+f5qxJgaepTq609LsLc2xqTnW4MJUAePmfNCrHHMiXjJBKl91Ppo2 - CsBcHy1XRHi2v7gmU1lTM8uNZcTrhMMouQtXd3j1M1rvou0Ox4sojuNo/TRDYswqcGkIbtx0c0U6 - 5wC+Ho7ehq0uxk110aQXjEp2JUllI4w6ZrPsGzNSIjIGud3uDhnVwGSZbotnNppZ4AjE+QmHOJz7 - 74JhwHV2AMULDm6/ZwDOb6NbgwfvPOSoIi8t6FuUNwZ1uiHSLRYJadDQcTE3kch6l+xQQUo9DbJX - UFjsYOm/4OUHDKngI7LHT4HfZmuff90OLu6C7sZYONRsL0t/eQwkTp7LgrPWs+rz3SKHuukWsVX9 - i172lUOg7VfQFzGu68bAO1Bj9gzIq/9eRZezw7U37X8cWPhrBwUAAA== + H4sIAAAAAAAAA41UXU/bMBR951dYeaat44Sk6VupEBOIbgzGpE1T5MY2NaR2ZTuFCvW/z3aSJgEm + LQ+Wcj98zz33+L6dABAUWBEdzMBv+wPAmz+tueTi2Zlbg4ustJEbqqw1WPxI1ovbl5CYsnrS15ch + 1Q/3JTJBk384bS9iXDxStVVcGJc4LVIGYxRFyRlm02kR0SgpGERpnNACJzEjBYsSQgpEQxZlNpwW + GYsYI/AMoyQNjhcXu12uqK5Kd6+oyvLoEdVmVcN87X2h/bpsvNP5VmqDy3yDTbF22ITs/PR1yxU2 + XIp8I4Vx/hAeK2yowUNyFH20sZ4aKVil3U8b7rjjZp8b+eIj+g5MiG1C58g5zuUrQB8p5MTfu0ie + ULmEi4uKRS8mTa/Q6vZnqnqMYEMfpdq7aGnWloIjYLPfUmcuFCXcDEls+x9wuMLiORd447Ouvt18 + /X45X4LFl/ndBTifL69PwXI8Hw/51EZRajo+91R3ASuFhe/jgWvcmastsahJjr06EAzPRjAaoeg+ + jGZxNovTcRimYRr/6pWqKbPxfXFagq2l30OtYuqY/cQcunohisAN5gLceej9wVhpGN/8p/NspFNI + 4mMyiELYzy5kJYza502Aq//hZRRY5ISu7DhmgOFS007ANe+DgfQEuafYPUIEUadI+xre9Rlwne+o + 4oxTx7tRVVfAqf/4doK6edBIEdg5gbo/4OADIoGQBniZ9Cbu4Ddyeo9/rShztEz8cpn8h3IVHaog + HoVwhNJ7mM3gdIaSMYRZklkVeBYP9vzj2Hq3puplNnZV87Us/V5rQbx5LGNODh5V468pb/N6C27S + bjs9aTJbw6GZc5NEuN5Wxir901JH76CQn/q/Mmqfbe5wcvgLinZLo6IFAAA= headers: content-encoding: [gzip] - content-length: ['553'] + content-length: ['671'] content-type: [application/json] status: {code: 200, message: OK} - request: - body: '{"links": {"customer": "/customers/CU6gH6X342hhVXOrrH7SVFzL"}, "cvv_result": - null, "number": "xxxxxxxxxxxx4242", "expiration_month": 12, "meta": {}, "id": - "CC5f7iWotJIWnjSrXPdO9KGA", "category": "other", "is_verified": true, "type": - "credit", "cvv_match": null, "bank_name": "", "avs_street_match": null, "brand": - "Visa", "updated_at": "2015-03-23T16:19:24.144416Z", "fingerprint": "1e2c425a579a1c7f27a037f0662b855035ceadbff18a0adbe785fb07e2afe1cc", - "can_debit": true, "customer": null, "name": null, "expiration_year": 2020, - "cvv": null, "avs_postal_match": null, "avs_result": "Postal code matches, but - street address not verified.", "can_credit": false, "created_at": "2015-03-23T16:19:24.144414Z", - "address": {"city": null, "line2": null, "line1": null, "state": null, "postal_code": - null, "country_code": null}}' + body: !!python/unicode '2' headers: {} - method: PUT - uri: https://api.balancedpayments.com:443/cards/CC5f7iWotJIWnjSrXPdO9KGA + method: POST + uri: https://api.sandbox.braintreegateway.com:443/merchants/j9gwdfjdkxymhdgr/customers response: body: string: !!binary | - H4sIAAAAAAAAA41U226bQBB9z1eseE5sWMDYfqsitelFSqS0jpWqQsvuYLaBBe0uVlyLf+8uFxs7 - TlU/WPYMZ+bMmcPsrxByKJFMOUv00/xBaN9+m3DOxYsNDwH7ZK10WYA0Uef2x2xzN1v7Ac6y1fpe - yrvocfXxzzenxzfXQ6GUiw3ISnKhLdADTAMckjBaEI9GKY6I60epO5vhZB6Grh9SICxJU29OXPMD - onmYJm4EmKTgUeocCtPtNpag6tzWFXWeHzKiLpKO5uvoE+AAH9Fkq+KqVJrkcUE0zc5rwGvFJdG8 - FHFRCm3zHj50KEATK85xTM5aWW7DNOJPpf7y+Un8fpTrB3a/+Prpw4g10bAp5c4+XerM0DwU1bsK - bJhKYFyfDnqRY0LESyxI0aJOR1NaAujLoyWSiJbtiityhNUVM9xYTNo9YdcLb1z/BvvfvdnSWyxx - OJm7czfyn0edGDMbOLcJ13a68UY6PwG+HPbehs1etJ3qrEi/MFqyC0la1kLLXTzKvjEjJSJmkBh1 - l0jLGo6W6VQ8sdHIAjsg1vbYxe7Yf2cMHa7iLUiecrD6njSwfju41XlonYcsVdSuFtQ1SmqNur0h - 0gmLRKnRUHEyNpGIe5csUUpydRwkk5Ca3s60fa+n/2FICf9cezDxgiDwgufu1W6Mpr+sBmcXorsj - E9s1zsq8PSkDiX3LZcJZ07Lq852QA250W6bDoVHTHjkEml6CHsS4qmoN77Q6ZE8atdt/D9HlzHDN - VfMX1oMJeB0FAAA= + H4sIAKLtTVUAA5SRQW6DMBBF9zkF8t41EBWVyJBdT5BuupsyA3GCDbJNA7cvplSpSruo5M387zfj + P5bHUbfRO1mnOlOw5CFmEZmqQ2Wagr2cnvkTO5Y7WQ3Od5psuYsiqbBMszzfZ/leirkI2uxVZzCe + z/Ulb25YX/A6TvqMjZXiuxtu18o6zw1oioxqC+btQEwsVgt/OVWnezDTRicNqt2o/bkz2x41jBvt + Rm9O+V/mWQJPyMFHfuqpYDiXXmliZRonjzyeT35KkkOaHOLsVYo7sPBDj//j78Dn/GXnvFbUogtK + SAXWq0r16y5TKX4oARQbMmRB5XkFFt36GrAWpjUqIFpyjjbeV6/w8x8AAAD//wMA5OomWiwCAAA= headers: + cache-control: ['max-age=0, private, must-revalidate'] content-encoding: [gzip] - content-length: ['581'] - content-type: [application/json] - status: {code: 200, message: OK} + content-type: [application/xml; charset=utf-8] + etag: ['"18e001fcefa80c8140bff63e5e33ff9c"'] + strict-transport-security: [max-age=31536000, max-age=31536000; includeSubDomains] + transfer-encoding: [chunked] + vary: [Accept-Encoding] + status: {code: 201, message: Created} - request: - body: null + body: !!python/unicode '269936932' headers: {} - method: GET - uri: https://api.balancedpayments.com:443/customers/CU6gH6X342hhVXOrrH7SVFzL/cards?limit=10&offset=0 + method: POST + uri: https://api.sandbox.braintreegateway.com:443/merchants/j9gwdfjdkxymhdgr/client_token response: body: string: !!binary | - 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 + evz69k7ePH79+IL+AwAA//8DAP9NzbN4BwAA headers: + cache-control: ['max-age=0, private, must-revalidate'] content-encoding: [gzip] - content-length: ['673'] - content-type: [application/json] - status: {code: 200, message: OK} + content-type: [application/xml; charset=utf-8] + etag: ['"433581f12e9ddc36d0a94b5309b1d589"'] + strict-transport-security: [max-age=31536000, max-age=31536000; includeSubDomains] + transfer-encoding: [chunked] + vary: [Accept-Encoding] + status: {code: 201, message: Created} - request: body: null headers: {} method: GET - uri: https://api.balancedpayments.com:443/customers/CU6gH6X342hhVXOrrH7SVFzL/cards?limit=10&offset=0 + uri: https://api.balancedpayments.com:443/cards/CC6j2lN0CEuf3wt77J2bQW7r response: body: string: !!binary | - H4sIAAAAAAAAA6VV32+bMBB+719h+WFPbQIOhCRSNU2Vtu6H1Erd0qrThAycG69gI2OiZlX+99kG - AknTadp4iMid7+67+z4fzycI4ZSqrMIL9N38QejZ/RpzzsWjNXcGe7KutCxAGSu++DZ9uJzeTQKy - Wi3vrpS6jG6W7399wW389rRLxLh4AFUqLrQN9IGkAQlpGM2pn0aMRNSbRMybTkkyC0NvEqZAs4Qx - f0Y98wLRLGSJFwGhDPw0xbvE6XodK6jq3OYVdZ7vPKIukgbm0+AJSED6aLqu4lJWmuZxQXW6OswB - TyVXVHMp4kIKbf0+2VUoQFM7nL5NnrmxXIQs4rdSf/p4K37eqLvr7Gr++cO7AWqq4UGqjT0t9crA - 3CXVmxKsOVWQcb3f6FGMCRWPsaCFi9pvrdIKQB9vLVFUOLRLXtE+rC4zgy2LqeOJeH545k3OyOSr - P1348wUJRzNv5kWT+0GlLDMMHMqEa9vdkJFGT0COm/2XZsOLtl0dJGkJS2V2xJnKWmi1iQfeF2JM - qYgzSMx0F0irGnrJNFPck9FAAhugVvbEI95QfwcIMa/iNSjOONj57hWwetupFV875SELFTlqoTpF - Sa1RwxuizWCRkBp1GUdDEYm4VckCMZpXfSMrBczUxmN3r8d/IUgFf6Q9GPlBEPjBfXO1t2amP+wM - cHcH3JBxTiunm3G3JUztV3ZEA+1tzguuz33vjWSsAn3utf1hAU/7Vxr3TbUr6N+Tu6oGqN8SiZvq - xtIZSgVrLmur6l4OWEtDmI1r+MeMq/9v2IzObZCDbdvs5FHGq7LWYIF0dD47Vkc824533hZQ4xnu - 6AEVbVzn3XajboNM0nglc/cdOFLKnmr9e8XcTXolpvXZDk+2vwHe2sczaQYAAA== + H4sIAAAAAAAAA41UXU/bMBR951dYeaat44Sk6VupEBOIbgzGpE1T5MY2NaR2ZTuFCvW/z3aSJgEm + LQ+Wcj98zz33+L6dABAUWBEdzMBv+wPAmz+tueTi2Zlbg4ustJEbqqw1WPxI1ovbl5CYsnrS15ch + 1Q/3JTJBk384bS9iXDxStVVcGJc4LVIGYxRFyRlm02kR0SgpGERpnNACJzEjBYsSQgpEQxZlNpwW + GYsYI/AMoyQNjhcXu12uqK5Kd6+oyvLoEdVmVcN87X2h/bpsvNP5VmqDy3yDTbF22ITs/PR1yxU2 + XIp8I4Vx/hAeK2yowUNyFH20sZ4aKVil3U8b7rjjZp8b+eIj+g5MiG1C58g5zuUrQB8p5MTfu0ie + ULmEi4uKRS8mTa/Q6vZnqnqMYEMfpdq7aGnWloIjYLPfUmcuFCXcDEls+x9wuMLiORd447Ouvt18 + /X45X4LFl/ndBTifL69PwXI8Hw/51EZRajo+91R3ASuFhe/jgWvcmastsahJjr06EAzPRjAaoeg+ + jGZxNovTcRimYRr/6pWqKbPxfXFagq2l30OtYuqY/cQcunohisAN5gLceej9wVhpGN/8p/NspFNI + 4mMyiELYzy5kJYza502Aq//hZRRY5ISu7DhmgOFS007ANe+DgfQEuafYPUIEUadI+xre9Rlwne+o + 4oxTx7tRVVfAqf/4doK6edBIEdg5gbo/4OADIoGQBniZ9Cbu4Ddyeo9/rShztEz8cpn8h3IVHaog + HoVwhNJ7mM3gdIaSMYRZklkVeBYP9vzj2Hq3puplNnZV87Us/V5rQbx5LGNODh5V468pb/N6C27S + bjs9aTJbw6GZc5NEuN5Wxir901JH76CQn/q/Mmqfbe5wcvgLinZLo6IFAAA= headers: content-encoding: [gzip] - content-length: ['673'] + content-length: ['671'] content-type: [application/json] status: {code: 200, message: OK} - request: - body: null + body: !!python/unicode '2' headers: {} - method: GET - uri: https://api.balancedpayments.com:443/cards/CC5f7iWotJIWnjSrXPdO9KGA + method: POST + uri: https://api.sandbox.braintreegateway.com:443/merchants/j9gwdfjdkxymhdgr/customers response: body: string: !!binary | - H4sIAAAAAAAAA41U226bQBB9z1eseE5sWMDYfqsitelFSqS0jpWqQsvuYLaBBe0uVlyLf+8uFxs7 - TlU/WPYMZ+bMmcPsrxByKJFMOUv00/xBaN9+m3DOxYsNDwH7ZK10WYA0Uef2x2xzN1v7Ac6y1fpe - yrvocfXxzzenxzfXQ6GUiw3ISnKhLdADTAMckjBaEI9GKY6I60epO5vhZB6Grh9SICxJU29OXPMD - onmYJm4EmKTgUeocCtPtNpag6tzWFXWeHzKiLpKO5uvoE+AAH9Fkq+KqVJrkcUE0zc5rwGvFJdG8 - FHFRCm3zHj50KEATK85xTM5aWW7DNOJPpf7y+Un8fpTrB3a/+Prpw4g10bAp5c4+XerM0DwU1bsK - bJhKYFyfDnqRY0LESyxI0aJOR1NaAujLoyWSiJbtiityhNUVM9xYTNo9YdcLb1z/BvvfvdnSWyxx - OJm7czfyn0edGDMbOLcJ13a68UY6PwG+HPbehs1etJ3qrEi/MFqyC0la1kLLXTzKvjEjJSJmkBh1 - l0jLGo6W6VQ8sdHIAjsg1vbYxe7Yf2cMHa7iLUiecrD6njSwfju41XlonYcsVdSuFtQ1SmqNur0h - 0gmLRKnRUHEyNpGIe5csUUpydRwkk5Ca3s60fa+n/2FICf9cezDxgiDwgufu1W6Mpr+sBmcXorsj - E9s1zsq8PSkDiX3LZcJZ07Lq852QA250W6bDoVHTHjkEml6CHsS4qmoN77Q6ZE8atdt/D9HlzHDN - VfMX1oMJeB0FAAA= + H4sIAKrtTVUAA5SRTW6DMBCF9zkF8t4lJonyI0N2PUG66W7KDOAEG2SbBm5fTKlSlXZRyZt5z9+M + 31iee11H72SdakzKxNOaRWTyBpUpU/ZyeeYHds5WMu+cbzTZbBVFUmG2P2w2u/1WyHgsgjZ6eQXG + 87G+Hss7Fle89YOusLQy/u6G24WyznMDmiKj6pR52xGLJ6uGv5y80S2YYaGTBlUv1LZqzLJHAf1C + u9ObU/6XeZbAE3LwkR9aShmOpVeaWJasxY6vx3O8CHFKxElsX2X8ACa+a/F//AP4nD/tnBeKanRB + CanAepWrdt5lIuMfSgDjBRmyoPI8B4tufg1YC8McFRAtOUcL76tX+PkPAAAA//8DAMJ8bnEsAgAA headers: + cache-control: ['max-age=0, private, must-revalidate'] content-encoding: [gzip] - content-length: ['581'] - content-type: [application/json] - status: {code: 200, message: OK} -- request: - body: null - headers: {} - method: DELETE - uri: https://api.balancedpayments.com:443/cards/CC5f7iWotJIWnjSrXPdO9KGA - response: - body: {string: !!python/unicode ''} - headers: - content-length: ['0'] - content-type: [application/json] - status: {code: 204, message: NO CONTENT} + content-type: [application/xml; charset=utf-8] + etag: ['"0befb0918dfb0d42253017ea05dfce7e"'] + strict-transport-security: [max-age=31536000, max-age=31536000; includeSubDomains] + transfer-encoding: [chunked] + vary: [Accept-Encoding] + status: {code: 201, message: Created} - request: - body: null + body: !!python/unicode '783357412' headers: {} - method: GET - uri: https://api.balancedpayments.com:443/bank_accounts/BA123123123 + method: POST + uri: https://api.sandbox.braintreegateway.com:443/merchants/j9gwdfjdkxymhdgr/client_token response: - body: {string: !!python/unicode "{\n \"errors\": [\n {\n \"status\": - \"Not Found\",\n \"category_code\": \"not-found\",\n \"description\": - \"

The requested URL was not found on the server.

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

Your request id - is OHMaec0b31cd17711e48c5306429171ffad.\",\n \"status_code\": 404,\n - \ \"category_type\": \"request\",\n \"request_id\": \"OHMaec0b31cd17711e48c5306429171ffad\"\n - \ }\n ]\n}"} + body: + string: !!binary | + H4sIAKztTVUAA7xV227bOBB9z1cEee9WouJsBCQp4tiSLVh0Ldu68E0k5epCSo4vun39Dh23TtBg + UWCBfRAIkcOZM2fODB++tVJc18lun1Xl443+l3ZznZSs4ln54/FmvbK+3N98e7p6YCJLysOXQ1Uk + 5dPV9fVDHYtj8pR0DiKh08eBeZzmVTd7cVIeehU1nG0iLU3te1IcCfI7NnG2tFxk88xJcT/VcO7l + sPbYng6wHDewIhJEBg4WXSS93F25XYRIgXOe4RWH/zXs+xkerRt3VXTuyNWIbYn5ystwP9Y2Nu5I + YGkk8DZRuDDd/LnFS61R39xftO6q6uBrcXaruXnR4T4a4NHzzh09N67V6rB2WGKdGV5NA78LkSi+ + r9it27s67hdtOIG4oylSZxFKU1p6mzhYmHEuSo48GUtvl0zEIba9komzv3KoR7LdRrq5I2FhupIM + WLDVcGj1LmIaL3lLsul+KnFNJdkS3e/oy/RuKlONT4b9PLuvo3C4nZU4pdLLqPHjGJVOGgcDjUlf + EAT5hjxNloOcIu0Ojxb9DOmCSVxFyqYzX+cBN4hNXol9uE0CvSI272YI7+PAP3LLTJld1Dwf1xEy + j0RCLiHU0BY1fcMFfto9CQYlCd1sLg4caixoSbZMmkeqfCi8F3wZnOfUFmDjDZktfCYbVfOKT7yG + 9VU9Q1YTLwc94Csiad7OpNNFgTjyiSNIwCG2b0RhcQQ8h3m+0NzOPIDG8ti2wMatP+U7M3/G3Ci+ + plmTRSHuSej1fujsQZtZPPE0NnHvZh3kbGABvv6Ny+ynlj+7r/ZnJRFU6vUf1ubkj0prn4TeNjKA + y3Kf8d99n/NYHICflE4E9BDu/zTGZtlk3E6hD/zxGvk5+Bce+AENCgJ1Ao518onNNN/+PZVtTdFB + Z9b/pEHDq9T9EHkbdsYxQ2cML44JfDVRKBrQnwW8ZdT2C8CpsdIXl7PmxCOxRc/sNk3WwFmwVnqb + MDV/wiHgOun4Te9joXwcOWha+WBSoCjAgw81HimN+rdRoDfUXp90ODOGjdIUD7FghVUq3Cftf+Dq + WdVPJLZ1ACxipnp6aWoUag2xlB7Ff9Himx3+pJ9wRQK8o4avzUrIF3KmL7/uFTHwGhneyyX+r/xT + arc1107YgUev48EauIK8pOgoGhyg77WVNOfQRwbM9N07/n+fAdKUBGYdlWsVV4dzwOsJMr5oAe5L + qGevNPg+3wVqtyrWMji9DwfAqqmeP/dCr7h/N3eHEcI1v9j3HN6QuF8YSbnN4tzpcO6/nmaAAe8O + 5BMZwmHIHFHkCcW/7+PxtNROcxcwnGrzvk8I9ClDiosL9nOsT7V80gbEocG9sqmJjLLN4vHx4evb + O3n18PXjC/oPAAAA//8DALFM6r94BwAA headers: - content-length: ['430'] - content-type: [application/json] - status: {code: 404, message: NOT FOUND} + cache-control: ['max-age=0, private, must-revalidate'] + content-encoding: [gzip] + content-type: [application/xml; charset=utf-8] + etag: ['"b5d9272abddafe4ab1a2b12bdfe3b700"'] + strict-transport-security: [max-age=31536000, max-age=31536000; includeSubDomains] + transfer-encoding: [chunked] + vary: [Accept-Encoding] + status: {code: 201, message: Created} - request: - body: null + body: !!python/unicode '4' headers: {} - method: GET - uri: https://api.balancedpayments.com:443/customers/CU6gH6X342hhVXOrrH7SVFzL + method: POST + uri: https://api.sandbox.braintreegateway.com:443/merchants/j9gwdfjdkxymhdgr/customers response: body: string: !!binary | - H4sIAAAAAAAAA41Uy27bMBC85ysEnWvrYSFqfA1Q5FCgh7ZB0KIQaJKNiEiky0dR19C/d2lLMk2q - YS467M4MubMjHm+SJMVGadFTqdJt8h0KSXI8faHFUU+hyk3XvZtqHeMvFjqBAKaEkdgHQp1QpRlH - mgk+qowiw6xm9gRpShqkAZGWeVGtinxV1l/yu23+fltW67yu6yr/ls4ULOmrlM36riyKYuNQiNg1 - veC69YdhxB57//X2+eH2aVOVbfv49EnKh/rz44e/Hy9n7lvB/QHTVtKflp7NDmZxJTAat4jrRmmk - jTUy5WLVI43by3E91cizmP6y0DwNHLSzHSiS/miIEEmVtynM9MEDwp5gpbRcLhdh2V7c9wJE9gIa - XYMFWWhiYbiWB7cbDLIzCu6hVLOUOqV40yGlK39M2iPWBUV2FbgBzvph82MndcJ7if56jnCagWun - PKvsOC927A/jihwiRpI0rejIaZVOFBwyI0Pm4M5JdjR2iL80CJ9MishcQwMlQncsJjFiFrju3/of - H5xfesEM+kdTySEFbxsmhAeXsrZFLDlDAiZham80jZBnVMDXEnGFsH28IhpXyEAHHgnDYzNMoAX2 - b3iZURe5gqQTLFAQkpzf9lfCOWICLjy1JJqnCRSw3xaCGQX/53Az/APeQA0dkwYAAA== + H4sIAK/tTVUAA5SRwVKDMBCG730KJvcIqbVTOoHefIJ68bayC6QlgUmChbeXIE4d0YMzuez/59vN + v5GnQTfRO1mnWpMx8ZCwiEzRojJVxl7Oz/zATvlGFr3zrSabb6JIKswPu3T/KJK9jKciaJNX1GA8 + n+pLWt2wvOB1GHWNlZXxdzfcLpV1nhvQFBnVZMzbnlg8Ww385RSt7sCMK500qGaldnVr1j1KGFba + jd6c8r/MswSekIOP/NhRxnAqvdLE8m0inngynfQsxHErjiJ9lfEdmPm+w//xd+Bz/rxzXipq0AUl + pALrVaG6ZZc7Gf9QAhivyJAFlecFWHTLa8BaGJeogGjJOVp5X73Cz38AAAD//wMAqkJ7liwCAAA= headers: + cache-control: ['max-age=0, private, must-revalidate'] content-encoding: [gzip] - content-length: ['505'] - content-type: [application/json] - status: {code: 200, message: OK} + content-type: [application/xml; charset=utf-8] + etag: ['"e774a13803f4bd8b30a253abf0c0dcb2"'] + strict-transport-security: [max-age=31536000, max-age=31536000; includeSubDomains] + transfer-encoding: [chunked] + vary: [Accept-Encoding] + status: {code: 201, message: Created} - request: - body: null + body: !!python/unicode '849631062' headers: {} - method: GET - uri: https://api.balancedpayments.com:443/cards/CC123123123123 + method: POST + uri: https://api.sandbox.braintreegateway.com:443/merchants/j9gwdfjdkxymhdgr/client_token response: - body: {string: !!python/unicode "{\n \"errors\": [\n {\n \"status\": - \"Not Found\",\n \"category_code\": \"not-found\",\n \"description\": - \"

The requested URL was not found on the server.

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

Your request id - is OHMaf611e6ad17711e48c5306429171ffad.\",\n \"status_code\": 404,\n - \ \"category_type\": \"request\",\n \"request_id\": \"OHMaf611e6ad17711e48c5306429171ffad\"\n - \ }\n ]\n}"} + body: + string: !!binary | + H4sIALHtTVUAA7xVXW+jOhB976+o+r53wSS9i9R21XxAQMHZkASw37AhBWKTNCEQ+PV3nGY3rba6 + WulK9wFFisdnzpw5M374fpLitk73h3xbPt7pf2l3t2nJt0levjzerZbWl293359uHrjI07L6Um03 + afl0c3v7UMfimD6lrYto5HZxaB6dYttOh26WRP6WGe4ulZam/velOFIUtHzi7lg5z2e5K2noISp9 + iaXTo0vSkWLeUnuuk2UgScdbD40bT/obPAoKWgwyLMf6bMn7pFu1VDo6Xj6fPBtLUnB9NgqEhyyx + tnFLQ0ujob8m0dz0iucTXmiN+mbB/OQtt+35G/b6sxFB3tLpzUarvTd6bjzrpMNviyXWueHXLAza + CInNj+WLNgN+3vIZRZNV440cpM4IyjJW+us4nJtxIcoE+TKW/j6diCq2/ZKLC1450Ik87Yhu7mm0 + MT1J+zzcaTiyOg9xLSmTE82dgyNxzSTdUT1o2dC5d2SmJZNBN8u/1SQa7KYlzpj0c2a8HEnpZnHY + 17gMBEVQb5Rk6aJfMKTd49G8myJdcIm3RMW05ussTAxq01dqV7001LfUTtopwoc4DI6JZWbc3tRJ + Ma4JMo9UQi0R9NAWNXvjBTinAw37JY28fCaqBHosWEl3XJpHpjAU3yu/HM4LZguI8QfcFgGXjer5 + Npn4De+29RRZTbzod8BvQ6TZm0q3JaE4JhNX0DCB3IFBos0R+FSzYq55rVmBx4rYtiDGqz/VOzd/ + 5lwrvZy8yUmEOxr5XRC5B/BmHk98jU+8+2kLNRtYANa/aZn/9PJn99X/05IKJvX6D3tzxmPSOqSR + vyMGaFke8uR37Esd8wr0ydhEwAzh7k9zrBdNntgZzEEwXqGgAHzhAw54UFDoE2is009inGL3tyNP + NUOVzq3/yYOGv1X3I+Sv+YXHFF04DF0T9GpIJBrwnwW65cwONsBT42UgrmfNWUdqi47bpyxdgWbh + SvltwtX+iQbA6+zjN7+PhcI4JuBphcGlQCTE/Q89HimPBj0S6g2zV2cfTo1BozyVRFjwjVUq3mfv + f9DqWfVPpLZVARcxVTO9MDUGvYZcyo/iv3jxLQ5/Mk94S0O8Z0agTUuoF2pmw1/3NjHoSgx/eM3/ + q/6M2ac60c7cQUe/TcIVaAV1SdEy1K9g7rWlNGcwRwbs9P07/X/fAdKUFHYdkyuVV4dz4OsLOr56 + Ae5L6GenPPi+3jk67VSuRXh+HyrgqqmZv8xCp7R/t3cHBOE6ucZ3ie30425upOUujwu3xUXwet4B + Brw7UA8xhMuROWLIF0r/IMBjp9TOexc4nHvzfk4ozClHSosr90uuT7189gbkYeE3FVNTSfL1/PHx + 4evbO3nz8PXjC/oPAAAA//8DAJHWtkJ4BwAA headers: - content-length: ['430'] - content-type: [application/json] - status: {code: 404, message: NOT FOUND} + cache-control: ['max-age=0, private, must-revalidate'] + content-encoding: [gzip] + content-type: [application/xml; charset=utf-8] + etag: ['"b502ff8553c663fa664a4bb95858ac11"'] + strict-transport-security: [max-age=31536000, max-age=31536000; includeSubDomains] + transfer-encoding: [chunked] + vary: [Accept-Encoding] + status: {code: 201, message: Created} - request: body: null headers: {} @@ -488,6 +515,65 @@ interactions: content-length: ['671'] content-type: [application/json] status: {code: 200, message: OK} +- request: + body: !!python/unicode '2' + headers: {} + method: POST + uri: https://api.sandbox.braintreegateway.com:443/merchants/j9gwdfjdkxymhdgr/customers + response: + body: + string: !!binary | + H4sIALTtTVUAA5RRQVKEMBC87yuo3GMWRHStwN58wXrxNjIDZCWBSoILv5cg1lqiB6vm0t3pmemJ + PI66jd7JOtWZnMU3exaRKTtUps7Z8+mJP7BjsZPl4HynyRa7KJIKiyzL0vQ+vpViBoGbtbIB4/mM + z4f6gtUZ38ZJN1hbKb6r4XWlrPPcgKbIqDZn3g7ExCK18JdSdroHM2140qDaDds3ndn2qGDccBd6 + dcr/Ms8SeEIOPvJTTznDGXqliRXJPr7j+7kOpzh+TOZKX6S4Ghb/0OP//FfD5/zl5rxS1KILTEgF + 1qtS9estEyl+MMEoNs6QBZXnJVh06zZgLUxrVEC05BxttK9e4ec/AAAA//8DAFoyPUMsAgAA + headers: + cache-control: ['max-age=0, private, must-revalidate'] + content-encoding: [gzip] + content-type: [application/xml; charset=utf-8] + etag: ['"4fceafd6131707e4d21c00f307c1f813"'] + strict-transport-security: [max-age=31536000, max-age=31536000; includeSubDomains] + transfer-encoding: [chunked] + vary: [Accept-Encoding] + status: {code: 201, message: Created} +- request: + body: !!python/unicode '666447132' + headers: {} + method: POST + uri: https://api.sandbox.braintreegateway.com:443/merchants/j9gwdfjdkxymhdgr/client_token + response: + body: + string: !!binary | + H4sIALftTVUAA7xVXW+jOhB976+o+r53wTS9i9R21TSBgIKzIQlgv2GbFIhN0nxA4NffcZrdtNrq + aqUr3QcLZI9nzpw5M77/flTyus62u2JdPdyYfxk311nF16KoXh5uFnPny7eb749X91wWWbX/sl+v + surx6vr6vk7lIXvMWh/RxO/S2D545bodP/u5SMI1s/xNphxD74dKHiiKWj7yN6yaFpPCOxLlWThe + GKTzTDwP4PtiBWVUYEQa3Dkl7BukHCJcTlv4hyXKyXx6S2MnD1CAJi5BdBC0uMsLPBAlmfvF0sUt + nBs0DpckmdpB+XTEM6PRaxJNj8F83eqFi9veZMBRMB82QTfdBoOnJnCOJnxbrLDJrbBmcdQmSK5+ + zInGYOBu2CWjRRMMPKTPCMpzVoXLNJ7aaSkrgUKVqnCbjeQ+dcOKy7O/qm8SddwQ097SZGUHivZ4 + vDFw4nQB4oaoxJEW3s5TuGaKbqgZtezZu/NUbohRv5sU32qS9DfjCudMhQWzXg6k8vM07hlcRZIi + yDcReTbrlQwZd3gw7cbIlFzhNdE2rf06iYVFXfpK3f1tFptr6op2jPAujaODcOycu6talMOaIPtA + FeSSQA1dWbM3XODnuKNxr6JJUEzkXkCNJavohiv7wLQPjfeCr4DzkrkSbMI+d2XEVQM199diFDa8 + W9dj5DTprNcBvhVR9u1Y+S2J5UGMfEljAbEjiySrA+DZT4D7oLX3oLEydR2wCepP+S7snzGXmi+v + aAqS4I4mYRcl/g60WaSj0OCj4G7cQs4WluDr37gsfmr5s/t6f1xRyZRZ/2FtTv6YcnZZEm6IBVxW + u0L87vucx3QP/ORsJKGHcPenMZazphBuDn0QDRcoKsG/DMEPaFBSqBNwbNJPbLxy87enjjVDe5M7 + /5MGrXCt7ycoXPIzjjE6Y3j2beCrIYlsQH8O8FYwN1oBToNXkbycNSceqSs77h7zbAGcxQuttxHX + 8yfpA66Tjt/0PpTax0GAprUPriQiMe59qPFAazS6JbHZMHdx0uHY6jdaUyLBkq+cSuM+af8DV0+6 + fjJznT1gkWPd0zPbYFBriKX1KP+LFt/s8Cf9hNc0xltmRca4gnwhZ/b8694qBV6JFT5f4v/KP2fu + sRbGCTvwGLYiXgBXkJeSLUO9PfS9MVf2BPrIgpm+fcf/7zNA2YrCrGNqoeOacA54Q0mHFy3AfQX1 + 7LQG3+c7RceNjjWL9fvg7wGroXv+3Aud5v7d3O3DVK7Fxb4TrtdLu6mVVZsiLf0Wl9HraQZY8O5A + PsSSPkf2gKFQav6jCA+9yjjNXcBwqs37PqHQpxxpLi7Yz7E+1fJJGxCHxd+0TU0VKZbTh4f7r2/v + 5NX9148v6D8AAAD//wMAgkWfxHgHAAA= + headers: + cache-control: ['max-age=0, private, must-revalidate'] + content-encoding: [gzip] + content-type: [application/xml; charset=utf-8] + etag: ['"5aabc18516a9cea04a44bdbb581e723e"'] + strict-transport-security: [max-age=31536000, max-age=31536000; includeSubDomains] + transfer-encoding: [chunked] + vary: [Accept-Encoding] + status: {code: 201, message: Created} - request: body: null headers: {} @@ -519,12 +605,12 @@ interactions: method: GET uri: https://api.balancedpayments.com:443/credits?meta%5Bcontains%5D=exchange_id&limit=25&offset=0 response: - body: {string: !!python/unicode "{\n \"credits\": [],\n \"meta\": {\n \"last\": - \"/credits?limit=25&meta%5Bcontains%5D=exchange_id&offset=0\",\n \"next\": - null,\n \"href\": \"/credits?limit=25&meta%5Bcontains%5D=exchange_id&offset=0\",\n - \ \"limit\": 25,\n \"offset\": 0,\n \"previous\": null,\n \"total\": - 0,\n \"first\": \"/credits?limit=25&meta%5Bcontains%5D=exchange_id&offset=0\"\n - \ },\n \"links\": {}\n}"} + body: {string: !!python/unicode "{\n \"credits\": [],\n \"meta\": {\n \"\ + last\": \"/credits?limit=25&meta%5Bcontains%5D=exchange_id&offset=0\",\n \ + \ \"next\": null,\n \"href\": \"/credits?limit=25&meta%5Bcontains%5D=exchange_id&offset=0\"\ + ,\n \"limit\": 25,\n \"offset\": 0,\n \"previous\": null,\n \"\ + total\": 0,\n \"first\": \"/credits?limit=25&meta%5Bcontains%5D=exchange_id&offset=0\"\ + \n },\n \"links\": {}\n}"} headers: content-length: ['360'] content-type: [application/json] @@ -535,38 +621,13 @@ interactions: method: GET uri: https://api.balancedpayments.com:443/debits?meta%5Bcontains%5D=exchange_id&limit=25&offset=0 response: - body: {string: !!python/unicode "{\n \"meta\": {\n \"last\": \"/debits?limit=25&meta%5Bcontains%5D=exchange_id&offset=0\",\n - \ \"next\": null,\n \"href\": \"/debits?limit=25&meta%5Bcontains%5D=exchange_id&offset=0\",\n - \ \"limit\": 25,\n \"offset\": 0,\n \"previous\": null,\n \"total\": - 0,\n \"first\": \"/debits?limit=25&meta%5Bcontains%5D=exchange_id&offset=0\"\n - \ },\n \"links\": {},\n \"debits\": []\n}"} + body: {string: !!python/unicode "{\n \"meta\": {\n \"last\": \"/debits?limit=25&meta%5Bcontains%5D=exchange_id&offset=0\"\ + ,\n \"next\": null,\n \"href\": \"/debits?limit=25&meta%5Bcontains%5D=exchange_id&offset=0\"\ + ,\n \"limit\": 25,\n \"offset\": 0,\n \"previous\": null,\n \"\ + total\": 0,\n \"first\": \"/debits?limit=25&meta%5Bcontains%5D=exchange_id&offset=0\"\ + \n },\n \"links\": {},\n \"debits\": []\n}"} headers: content-length: ['356'] content-type: [application/json] status: {code: 200, message: OK} -- request: - body: null - headers: {} - method: GET - uri: https://api.balancedpayments.com:443/cards/CC6j2lN0CEuf3wt77J2bQW7r - response: - body: - string: !!binary | - H4sIAAAAAAAAA41UXU/bMBR951dYeaat44Sk6VupEBOIbgzGpE1T5MY2NaR2ZTuFCvW/z3aSJgEm - LQ+Wcj98zz33+L6dABAUWBEdzMBv+wPAmz+tueTi2Zlbg4ustJEbqqw1WPxI1ovbl5CYsnrS15ch - 1Q/3JTJBk384bS9iXDxStVVcGJc4LVIGYxRFyRlm02kR0SgpGERpnNACJzEjBYsSQgpEQxZlNpwW - GYsYI/AMoyQNjhcXu12uqK5Kd6+oyvLoEdVmVcN87X2h/bpsvNP5VmqDy3yDTbF22ITs/PR1yxU2 - XIp8I4Vx/hAeK2yowUNyFH20sZ4aKVil3U8b7rjjZp8b+eIj+g5MiG1C58g5zuUrQB8p5MTfu0ie - ULmEi4uKRS8mTa/Q6vZnqnqMYEMfpdq7aGnWloIjYLPfUmcuFCXcDEls+x9wuMLiORd447Ouvt18 - /X45X4LFl/ndBTifL69PwXI8Hw/51EZRajo+91R3ASuFhe/jgWvcmastsahJjr06EAzPRjAaoeg+ - jGZxNovTcRimYRr/6pWqKbPxfXFagq2l30OtYuqY/cQcunohisAN5gLceej9wVhpGN/8p/NspFNI - 4mMyiELYzy5kJYza502Aq//hZRRY5ISu7DhmgOFS007ANe+DgfQEuafYPUIEUadI+xre9Rlwne+o - 4oxTx7tRVVfAqf/4doK6edBIEdg5gbo/4OADIoGQBniZ9Cbu4Ddyeo9/rShztEz8cpn8h3IVHaog - HoVwhNJ7mM3gdIaSMYRZklkVeBYP9vzj2Hq3puplNnZV87Us/V5rQbx5LGNODh5V468pb/N6C27S - bjs9aTJbw6GZc5NEuN5Wxir901JH76CQn/q/Mmqfbe5wcvgLinZLo6IFAAA= - headers: - content-encoding: [gzip] - content-length: ['671'] - content-type: [application/json] - status: {code: 200, message: OK} version: 1 From f1b28140fd6f6a119a210420225e63911173d108 Mon Sep 17 00:00:00 2001 From: Rohit Paul Kuruvilla Date: Sat, 9 May 2015 17:50:13 +0530 Subject: [PATCH 17/25] Rename BalancedHarness to BillingHarness --- gratipay/testing/__init__.py | 4 ++-- gratipay/testing/{balanced.py => billing.py} | 8 ++++---- .../fixtures/{BalancedHarness.yml => BillingHarness.yml} | 0 tests/py/test_billing_exchanges.py | 8 ++++---- tests/py/test_billing_payday.py | 6 +++--- tests/py/test_routes.py | 4 ++-- 6 files changed, 15 insertions(+), 15 deletions(-) rename gratipay/testing/{balanced.py => billing.py} (94%) rename tests/py/fixtures/{BalancedHarness.yml => BillingHarness.yml} (100%) diff --git a/gratipay/testing/__init__.py b/gratipay/testing/__init__.py index 29713d2385..60eb5f3eba 100644 --- a/gratipay/testing/__init__.py +++ b/gratipay/testing/__init__.py @@ -196,8 +196,8 @@ def make_exchange(self, route, amount, fee, participant, status='succeeded', err network = route route = ExchangeRoute.from_network(participant, network) if not route: - from .balanced import BalancedHarness - route = ExchangeRoute.insert(participant, network, BalancedHarness.card_href) + from .balanced import BillingHarness + route = ExchangeRoute.insert(participant, network, BillingHarness.card_href) assert route e_id = record_exchange(self.db, route, amount, fee, participant, 'pre') record_exchange_result(self.db, e_id, status, error, participant) diff --git a/gratipay/testing/balanced.py b/gratipay/testing/billing.py similarity index 94% rename from gratipay/testing/balanced.py rename to gratipay/testing/billing.py index dfbb1dd8ca..129a3ec0d0 100644 --- a/gratipay/testing/balanced.py +++ b/gratipay/testing/billing.py @@ -9,7 +9,7 @@ from gratipay.testing.vcr import use_cassette -class BalancedHarness(Harness): +class BillingHarness(Harness): def setUp(self): self.david = self.make_participant('david', is_suspicious=False, @@ -32,11 +32,11 @@ def tearDownClass(cls): for t in itertools.chain(credits, debits): t.meta.pop('exchange_id') t.save() - super(BalancedHarness, cls).tearDownClass() + super(BillingHarness, cls).tearDownClass() -with use_cassette('BalancedHarness'): - cls = BalancedHarness +with use_cassette('BillingHarness'): + cls = BillingHarness balanced.configure(balanced.APIKey().save().secret) mp = balanced.Marketplace.my_marketplace if not mp: diff --git a/tests/py/fixtures/BalancedHarness.yml b/tests/py/fixtures/BillingHarness.yml similarity index 100% rename from tests/py/fixtures/BalancedHarness.yml rename to tests/py/fixtures/BillingHarness.yml diff --git a/tests/py/test_billing_exchanges.py b/tests/py/test_billing_exchanges.py index 478aeff4bb..059db83c69 100644 --- a/tests/py/test_billing_exchanges.py +++ b/tests/py/test_billing_exchanges.py @@ -22,10 +22,10 @@ from gratipay.models.exchange_route import ExchangeRoute from gratipay.models.participant import Participant from gratipay.testing import Foobar, Harness -from gratipay.testing.balanced import BalancedHarness +from gratipay.testing.billing import BillingHarness -class TestCredits(BalancedHarness): +class TestCredits(BillingHarness): def test_ach_credit_withhold(self): self.make_exchange('balanced-cc', 27, 0, self.homer) @@ -61,7 +61,7 @@ def test_ach_credit_invalidated_bank_account(self): assert error == 'No bank account' -class TestCardHolds(BalancedHarness): +class TestCardHolds(BillingHarness): def test_create_card_hold_for_suspicious_raises_NotWhitelisted(self): bob = self.make_participant('bob', is_suspicious=True, @@ -335,7 +335,7 @@ def test_record_exchange_result_updates_balance_for_positive_amounts(self): assert alice.balance == D('35.59') -class TestSyncWithBalanced(BalancedHarness): +class TestSyncWithBalanced(BillingHarness): def test_sync_with_balanced(self): with mock.patch('gratipay.billing.exchanges.record_exchange_result') as rer: diff --git a/tests/py/test_billing_payday.py b/tests/py/test_billing_payday.py index 5bb4a82730..049947b54e 100644 --- a/tests/py/test_billing_payday.py +++ b/tests/py/test_billing_payday.py @@ -11,11 +11,11 @@ from gratipay.exceptions import NegativeBalance from gratipay.models.participant import Participant from gratipay.testing import Foobar, Harness -from gratipay.testing.balanced import BalancedHarness +from gratipay.testing.billing import BillingHarness from gratipay.testing.emails import EmailHarness -class TestPayday(BalancedHarness): +class TestPayday(BillingHarness): @mock.patch.object(Payday, 'fetch_card_holds') def test_payday_moves_money(self, fch): @@ -253,7 +253,7 @@ def test_payday(self, payin, log): assert payin.call_count == 1 -class TestPayin(BalancedHarness): +class TestPayin(BillingHarness): def create_card_holds(self): payday = Payday.start() diff --git a/tests/py/test_routes.py b/tests/py/test_routes.py index 2de83d42e7..11f9a4f717 100644 --- a/tests/py/test_routes.py +++ b/tests/py/test_routes.py @@ -3,12 +3,12 @@ import balanced import mock -from gratipay.testing.balanced import BalancedHarness +from gratipay.testing.billing import BillingHarness from gratipay.models.exchange_route import ExchangeRoute from gratipay.models.participant import Participant -class TestRoutes(BalancedHarness): +class TestRoutes(BillingHarness): def hit(self, username, action, network, address, expected=200): r = self.client.POST('/%s/routes/%s.json' % (username, action), From 3d020dcede77e5813ca789fb1c1517b898308f6e Mon Sep 17 00:00:00 2001 From: Rohit Paul Kuruvilla Date: Sun, 10 May 2015 13:45:10 +0530 Subject: [PATCH 18/25] Don't actually create card for dummy exchanges --- gratipay/testing/__init__.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/gratipay/testing/__init__.py b/gratipay/testing/__init__.py index 60eb5f3eba..e0b79ffa36 100644 --- a/gratipay/testing/__init__.py +++ b/gratipay/testing/__init__.py @@ -196,8 +196,7 @@ def make_exchange(self, route, amount, fee, participant, status='succeeded', err network = route route = ExchangeRoute.from_network(participant, network) if not route: - from .balanced import BillingHarness - route = ExchangeRoute.insert(participant, network, BillingHarness.card_href) + route = ExchangeRoute.insert(participant, network, 'dummy-address') assert route e_id = record_exchange(self.db, route, amount, fee, participant, 'pre') record_exchange_result(self.db, e_id, status, error, participant) From 887222be7a7c81b3d9cb002fa6f098fff7ee304f Mon Sep 17 00:00:00 2001 From: Rohit Paul Kuruvilla Date: Sun, 10 May 2015 14:04:20 +0530 Subject: [PATCH 19/25] Add braintree customer to BillingHarness --- gratipay/testing/billing.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/gratipay/testing/billing.py b/gratipay/testing/billing.py index 129a3ec0d0..c07f29ab2f 100644 --- a/gratipay/testing/billing.py +++ b/gratipay/testing/billing.py @@ -3,6 +3,7 @@ import itertools import balanced +import braintree from gratipay.models.exchange_route import ExchangeRoute from gratipay.testing import Harness @@ -12,18 +13,28 @@ class BillingHarness(Harness): def setUp(self): + # Balanced Customer without funding instruments self.david = self.make_participant('david', is_suspicious=False, claimed_time='now', balanced_customer_href=self.david_href) + + # Balanced Customer with CC attached self.janet = self.make_participant('janet', is_suspicious=False, claimed_time='now', balanced_customer_href=self.janet_href) self.janet_route = ExchangeRoute.insert(self.janet, 'balanced-cc', self.card_href) + + # Balanced Customer with BA attached self.homer = self.make_participant('homer', is_suspicious=False, claimed_time='now', balanced_customer_href=self.homer_href) self.homer_route = ExchangeRoute.insert(self.homer, 'balanced-ba', self.bank_account_href) + # Braintree Customer without funding instruments + self.bob = self.make_participant('bob', is_suspicious=False, + claimed_time='now', + braintree_customer_id=self.bob_bt_id) + @classmethod def tearDownClass(cls): has_exchange_id = balanced.Transaction.f.meta.contains('exchange_id') @@ -74,3 +85,5 @@ def tearDownClass(cls): ).save() cls.bank_account.associate_to_customer(cls.homer_href) cls.bank_account_href = unicode(cls.bank_account.href) + + cls.bob_bt_id = braintree.Customer.create().customer.id From a268ffa0ce085be403e8623b0bbe8c43a17de56f Mon Sep 17 00:00:00 2001 From: Rohit Paul Kuruvilla Date: Sun, 10 May 2015 14:04:57 +0530 Subject: [PATCH 20/25] Add test for associate a new balanced card (should fail) --- tests/py/test_routes.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/py/test_routes.py b/tests/py/test_routes.py index 11f9a4f717..6e3c5b3557 100644 --- a/tests/py/test_routes.py +++ b/tests/py/test_routes.py @@ -2,6 +2,7 @@ import balanced import mock +import pytest from gratipay.testing.billing import BillingHarness from gratipay.models.exchange_route import ExchangeRoute @@ -17,6 +18,19 @@ def hit(self, username, action, network, address, expected=200): assert r.code == expected return r + # Remove this once we've dumped 'balanced-cc' altogether + def test_associate_balanced_card_should_fail(self): + card = balanced.Card( + number='4242424242424242', + expiration_year=2020, + expiration_month=12 + ).save() + customer = self.david.get_balanced_account() + self.hit('david', 'associate', 'balanced-cc', card.href, expected=400) + + cards = customer.cards.all() + assert len(cards) == 0 + def test_associate_and_delete_valid_card(self): card = balanced.Card( number='4242424242424242', From 0b88399f2dc6fcf6fcd47b9dc1e4990fdffe2ee8 Mon Sep 17 00:00:00 2001 From: Rohit Paul Kuruvilla Date: Sun, 10 May 2015 14:33:04 +0530 Subject: [PATCH 21/25] Add tests for braintree cards --- tests/py/test_routes.py | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/tests/py/test_routes.py b/tests/py/test_routes.py index 6e3c5b3557..cb62c0c83f 100644 --- a/tests/py/test_routes.py +++ b/tests/py/test_routes.py @@ -1,8 +1,8 @@ from __future__ import absolute_import, division, print_function, unicode_literals import balanced +from braintree.test.nonces import Nonces import mock -import pytest from gratipay.testing.billing import BillingHarness from gratipay.models.exchange_route import ExchangeRoute @@ -32,29 +32,25 @@ def test_associate_balanced_card_should_fail(self): assert len(cards) == 0 def test_associate_and_delete_valid_card(self): - card = balanced.Card( - number='4242424242424242', - expiration_year=2020, - expiration_month=12 - ).save() - customer = self.david.get_balanced_account() - self.hit('david', 'associate', 'balanced-cc', card.href) + self.hit('bob', 'associate', 'braintree-cc', Nonces.Transactable) - cards = customer.cards.all() + customer = self.bob.get_braintree_account() + cards = customer.credit_cards assert len(cards) == 1 - assert cards[0].href == card.href + assert self.bob.get_credit_card_error() == '' - assert self.david.get_credit_card_error() == '' + self.hit('bob', 'delete', 'braintree-cc', cards[0].token) - self.hit('david', 'delete', 'balanced-cc', card.href) + customer = self.bob.get_braintree_account() + assert len(customer.credit_cards) == 0 - david = Participant.from_username('david') - assert david.get_credit_card_error() == 'invalidated' - assert david.balanced_customer_href + bob = Participant.from_username('bob') + assert bob.get_credit_card_error() == 'invalidated' + assert bob.braintree_customer_id def test_associate_invalid_card(self): - self.hit('david', 'associate', 'balanced-cc', '/cards/CC123123123123', expected=400) - assert self.david.get_credit_card_error() is None + self.hit('bob', 'associate', 'braintree-cc', 'an-invalid-nonce', expected=400) + assert self.bob.get_credit_card_error() is None def test_associate_and_delete_bank_account_valid(self): bank_account = balanced.BankAccount( name='Alice G. Krebs' @@ -125,7 +121,7 @@ def test_credit_card_page_shows_card_missing(self): actual = self.client.GET('/alice/routes/credit-card.html', auth_as='alice').body.decode('utf8') assert expected in actual - def test_credit_card_page_loads_when_there_is_a_card(self): + def test_credit_card_page_loads_when_there_is_a_balanced_card(self): expected = 'Your credit card is working' actual = self.client.GET('/janet/routes/credit-card.html', auth_as='janet').body.decode('utf8') assert expected in actual @@ -134,13 +130,13 @@ def test_credit_card_page_shows_details_for_balanced_cards(self): response = self.client.GET('/janet/routes/credit-card.html', auth_as='janet').body.decode('utf8') assert self.card.number in response - def test_credit_card_page_shows_card_failing(self): + def test_credit_card_page_shows_when_balanced_card_is_failing(self): ExchangeRoute.from_network(self.janet, 'balanced-cc').update_error('Some error') expected = 'Your credit card is failing' actual = self.client.GET('/janet/routes/credit-card.html', auth_as='janet').body.decode('utf8') assert expected in actual - def test_receipt_page_loads(self): + def test_receipt_page_loads_for_balanced_cards(self): ex_id = self.make_exchange('balanced-cc', 113, 30, self.janet) url_receipt = '/janet/receipts/{}.html'.format(ex_id) actual = self.client.GET(url_receipt, auth_as='janet').body.decode('utf8') From e51c845a5133b468ec106d0c8f6e289ab7ec80ae Mon Sep 17 00:00:00 2001 From: Rohit Paul Kuruvilla Date: Sun, 10 May 2015 15:35:17 +0530 Subject: [PATCH 22/25] Rename participants in BillingHarness to avoid conflicts --- gratipay/testing/billing.py | 19 ++++++++++++++++--- tests/py/test_routes.py | 20 ++++++++++---------- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/gratipay/testing/billing.py b/gratipay/testing/billing.py index c07f29ab2f..56f73323fc 100644 --- a/gratipay/testing/billing.py +++ b/gratipay/testing/billing.py @@ -4,6 +4,7 @@ import balanced import braintree +from braintree.test.nonces import Nonces from gratipay.models.exchange_route import ExchangeRoute from gratipay.testing import Harness @@ -31,9 +32,14 @@ def setUp(self): self.homer_route = ExchangeRoute.insert(self.homer, 'balanced-ba', self.bank_account_href) # Braintree Customer without funding instruments - self.bob = self.make_participant('bob', is_suspicious=False, + self.roman = self.make_participant('roman', is_suspicious=False, claimed_time='now', - braintree_customer_id=self.bob_bt_id) + braintree_customer_id=self.roman_bt_id) + # Braintree Customer with CC attached + self.obama = self.make_participant('obama', is_suspicious=False, + claimed_time='now', + braintree_customer_id=self.obama_bt_id) + self.obama_route = ExchangeRoute.insert(self.obama, 'braintree-cc', self.obama_cc_token) @classmethod def tearDownClass(cls): @@ -86,4 +92,11 @@ def tearDownClass(cls): cls.bank_account.associate_to_customer(cls.homer_href) cls.bank_account_href = unicode(cls.bank_account.href) - cls.bob_bt_id = braintree.Customer.create().customer.id + cls.roman_bt_id = braintree.Customer.create().customer.id + + cls.obama_bt_id = braintree.Customer.create().customer.id + + cls.obama_cc_token = braintree.PaymentMethod.create({ + "customer_id": cls.obama_bt_id, + "payment_method_nonce": Nonces.Transactable + }).payment_method.token diff --git a/tests/py/test_routes.py b/tests/py/test_routes.py index cb62c0c83f..64dd05594c 100644 --- a/tests/py/test_routes.py +++ b/tests/py/test_routes.py @@ -32,25 +32,25 @@ def test_associate_balanced_card_should_fail(self): assert len(cards) == 0 def test_associate_and_delete_valid_card(self): - self.hit('bob', 'associate', 'braintree-cc', Nonces.Transactable) + self.hit('roman', 'associate', 'braintree-cc', Nonces.Transactable) - customer = self.bob.get_braintree_account() + customer = self.roman.get_braintree_account() cards = customer.credit_cards assert len(cards) == 1 - assert self.bob.get_credit_card_error() == '' + assert self.roman.get_credit_card_error() == '' - self.hit('bob', 'delete', 'braintree-cc', cards[0].token) + self.hit('roman', 'delete', 'braintree-cc', cards[0].token) - customer = self.bob.get_braintree_account() + customer = self.roman.get_braintree_account() assert len(customer.credit_cards) == 0 - bob = Participant.from_username('bob') - assert bob.get_credit_card_error() == 'invalidated' - assert bob.braintree_customer_id + roman = Participant.from_username('roman') + assert roman.get_credit_card_error() == 'invalidated' + assert roman.braintree_customer_id def test_associate_invalid_card(self): - self.hit('bob', 'associate', 'braintree-cc', 'an-invalid-nonce', expected=400) - assert self.bob.get_credit_card_error() is None + self.hit('roman', 'associate', 'braintree-cc', 'an-invalid-nonce', expected=400) + assert self.roman.get_credit_card_error() is None def test_associate_and_delete_bank_account_valid(self): bank_account = balanced.BankAccount( name='Alice G. Krebs' From c4bbe41424011745c9720279b63c76196a9b229e Mon Sep 17 00:00:00 2001 From: Rohit Paul Kuruvilla Date: Sun, 10 May 2015 15:12:57 +0530 Subject: [PATCH 23/25] add tests for credit card page --- gratipay/testing/billing.py | 6 ++++-- tests/py/test_routes.py | 9 +++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/gratipay/testing/billing.py b/gratipay/testing/billing.py index 56f73323fc..b3ac0300a0 100644 --- a/gratipay/testing/billing.py +++ b/gratipay/testing/billing.py @@ -96,7 +96,9 @@ def tearDownClass(cls): cls.obama_bt_id = braintree.Customer.create().customer.id - cls.obama_cc_token = braintree.PaymentMethod.create({ + cls.bt_card = braintree.PaymentMethod.create({ "customer_id": cls.obama_bt_id, "payment_method_nonce": Nonces.Transactable - }).payment_method.token + }).payment_method + + cls.obama_cc_token = cls.bt_card.token diff --git a/tests/py/test_routes.py b/tests/py/test_routes.py index 64dd05594c..4578d5656e 100644 --- a/tests/py/test_routes.py +++ b/tests/py/test_routes.py @@ -121,6 +121,15 @@ def test_credit_card_page_shows_card_missing(self): actual = self.client.GET('/alice/routes/credit-card.html', auth_as='alice').body.decode('utf8') assert expected in actual + def test_credit_card_page_loads_when_there_is_a_braintree_card(self): + expected = 'Your credit card is working' + actual = self.client.GET('/obama/routes/credit-card.html', auth_as='obama').body.decode('utf8') + assert expected in actual + + def test_credit_card_page_shows_details_for_braintree_cards(self): + response = self.client.GET('/obama/routes/credit-card.html', auth_as='obama').body.decode('utf8') + assert self.bt_card.masked_number in response + def test_credit_card_page_loads_when_there_is_a_balanced_card(self): expected = 'Your credit card is working' actual = self.client.GET('/janet/routes/credit-card.html', auth_as='janet').body.decode('utf8') From 4a9904684ef7e14ba0e57973a853434c68ed560b Mon Sep 17 00:00:00 2001 From: Rohit Paul Kuruvilla Date: Sun, 10 May 2015 15:30:27 +0530 Subject: [PATCH 24/25] Fix receipt page for braintree cards --- gratipay/billing/instruments.py | 19 +++++++++++--- tests/py/test_routes.py | 6 +++++ .../receipts/%exchange_id.int.html.spt | 25 ++++++++++--------- 3 files changed, 35 insertions(+), 15 deletions(-) diff --git a/gratipay/billing/instruments.py b/gratipay/billing/instruments.py index 2f1d74846e..ff5db1249c 100644 --- a/gratipay/billing/instruments.py +++ b/gratipay/billing/instruments.py @@ -4,10 +4,16 @@ class CreditCard: def __init__(self, *args, **kwargs): fields = [ + 'card_type', 'number', 'expiration_year', 'expiration_month', + 'address_line1', + 'address_line2', + 'address_city', + 'address_state', 'address_postal_code', + 'address_country_code', 'cardholder_name' ] for field in fields: @@ -18,19 +24,26 @@ def from_route(cls, route): if route.network == 'braintree-cc': card = braintree.PaymentMethod.find(route.address) return cls( + card_type=card.card_type, number=card.masked_number, expiration_month=card.expiration_month, expiration_year=card.expiration_year, - address_postal_code=card.billing_address.postal_code, - cardholder_name=card.cardholder_name + cardholder_name=card.cardholder_name, + address_postal_code=card.billing_address.postal_code ) else: assert route.network == 'balanced-cc' card = balanced.Card.fetch(route.address) return cls( + card_type=card.brand, number=card.number, expiration_month=card.expiration_month, expiration_year=card.expiration_year, + cardholder_name=card.name, + address_line1=card.address['line1'], + address_line2=card.address['line2'], + address_city=card.address['city'], + address_state=card.address['state'], address_postal_code=card.address['postal_code'], - cardholder_name=card.name + address_country_code=card.address['country_code'], ) diff --git a/tests/py/test_routes.py b/tests/py/test_routes.py index 4578d5656e..b808f78b9b 100644 --- a/tests/py/test_routes.py +++ b/tests/py/test_routes.py @@ -130,6 +130,12 @@ def test_credit_card_page_shows_details_for_braintree_cards(self): response = self.client.GET('/obama/routes/credit-card.html', auth_as='obama').body.decode('utf8') assert self.bt_card.masked_number in response + def test_receipt_page_loads_for_braintree_cards(self): + ex_id = self.make_exchange(self.obama_route, 113, 30, self.obama) + url_receipt = '/obama/receipts/{}.html'.format(ex_id) + actual = self.client.GET(url_receipt, auth_as='obama').body.decode('utf8') + assert self.bt_card.card_type in actual + def test_credit_card_page_loads_when_there_is_a_balanced_card(self): expected = 'Your credit card is working' actual = self.client.GET('/janet/routes/credit-card.html', auth_as='janet').body.decode('utf8') diff --git a/www/%username/receipts/%exchange_id.int.html.spt b/www/%username/receipts/%exchange_id.int.html.spt index 2cb97000bd..92b0b2617d 100644 --- a/www/%username/receipts/%exchange_id.int.html.spt +++ b/www/%username/receipts/%exchange_id.int.html.spt @@ -3,6 +3,7 @@ import balanced from aspen import Response from gratipay.models.exchange_route import ExchangeRoute from gratipay.utils import get_participant +from gratipay.billing.instruments import CreditCard [-------------------] @@ -19,10 +20,10 @@ if exchange is None: raise Response(404) route = ExchangeRoute.from_id(exchange.route) -if route and route.network == 'balanced-cc': - card = balanced.Card.fetch(route.address) +if route: + card = CreditCard.from_route(route) else: - card = balanced.Card(address={}, meta={}) + card = CreditCard() [-------------------]