From 8c20339618d149b34ec8823bb63a74a0501c4dec Mon Sep 17 00:00:00 2001 From: eric Date: Sat, 13 Oct 2012 13:35:41 -0400 Subject: [PATCH 1/5] typo --- frontend/templates/donation_user_error.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/templates/donation_user_error.html b/frontend/templates/donation_user_error.html index 1951a2d8..3bfa3e26 100644 --- a/frontend/templates/donation_user_error.html +++ b/frontend/templates/donation_user_error.html @@ -17,7 +17,7 @@

Wrong user for donation credit

-

Unglue.it would like to process your donation credit, but you are currently logged in as {{request.user.username}}. Your donation credit from {{nonprofit.name}} for ${{ envelope.amount }}.{{ envelope.cents }} is designated for {{ envelope.username }}. Do record your credit, you need to log out, and then log in as {{ envelope.username }}. If you have any problem, don't hesitate to contact unglue.it support. +

Unglue.it would like to process your donation credit, but you are currently logged in as {{request.user.username}}. Your donation credit from {{nonprofit.name}} for ${{ envelope.amount }}.{{ envelope.cents }} is designated for {{ envelope.username }}. To record your credit, you need to log out, and then log in as {{ envelope.username }}. If you have any problem, don't hesitate to contact unglue.it support.

From dbacb5a96d58361e59db826c190ad7a3e2a58328 Mon Sep 17 00:00:00 2001 From: eric Date: Sat, 13 Oct 2012 13:38:39 -0400 Subject: [PATCH 2/5] max_amount, not amount before the authorization --- frontend/views.py | 2 +- payment/manager.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/views.py b/frontend/views.py index cc96ee37..75ad8337 100755 --- a/frontend/views.py +++ b/frontend/views.py @@ -798,8 +798,8 @@ class FundPledgeView(FormView): # GOAL: deactivate any older accounts associated with user # with the Account in hand, now authorize transaction - transaction.amount = preapproval_amount t, url = p.authorize(transaction) + self.transaction.max_amount = preapproval_amount logger.info("t, url: {0} {1}".format(t, url)) # redirecting user to pledge_complete on successful preapproval (in the case of stripe) diff --git a/payment/manager.py b/payment/manager.py index c5d83f5d..b1b6e0fd 100644 --- a/payment/manager.py +++ b/payment/manager.py @@ -567,7 +567,7 @@ class PaymentManager( object ): urllib.urlencode({'tid':transaction.id})) return_url = urlparse.urljoin(settings.BASE_URL, return_path) - p = transaction.get_payment_class().Preapproval(transaction, transaction.amount, expiry, return_url=return_url, paymentReason=paymentReason) + p = transaction.get_payment_class().Preapproval(transaction, transaction.max_amount, expiry, return_url=return_url, paymentReason=paymentReason) # Create a response for this envelope = p.envelope() From 674637f61672f6d9196f0cff23f9d8f1b90fcc66 Mon Sep 17 00:00:00 2001 From: eric Date: Sat, 13 Oct 2012 13:40:36 -0400 Subject: [PATCH 3/5] we weren't checking that request.user==transaction.user --- frontend/templates/pledge_user_error.html | 29 +++++++++++++++++++++++ frontend/views.py | 4 ++++ 2 files changed, 33 insertions(+) create mode 100644 frontend/templates/pledge_user_error.html diff --git a/frontend/templates/pledge_user_error.html b/frontend/templates/pledge_user_error.html new file mode 100644 index 00000000..ae060302 --- /dev/null +++ b/frontend/templates/pledge_user_error.html @@ -0,0 +1,29 @@ +{% extends "basepledge.html" %} +{% load humanize %} + +{% block title %}Please Log in as...{% endblock %} + +{% block extra_extra_head %} + + + +{% endblock %} + +{% block doccontent %} +
+ +
+
+ +

Error: Wrong user for a pledge transaction

+
+

Unglue.it would like to process your pledge, but you are currently logged in as {{request.user.username}}. To complete your pledge, you need to log out, and then log in as {{ transaction.user.username }}. If you have any problem, don't hesitate to contact unglue.it support. +

+
+
+ +
+
+{% endblock %} + + diff --git a/frontend/views.py b/frontend/views.py index 75ad8337..ada67560 100755 --- a/frontend/views.py +++ b/frontend/views.py @@ -783,6 +783,10 @@ class FundPledgeView(FormView): logger.info('stripe_token:{0}, preapproval_amount:{1}'.format(stripe_token, preapproval_amount)) + if self.transaction.user.id != self.request.user.id: + # trouble! + return render(request, "pledge_user_error.html", {'transaction': self.transaction }) + p = PaymentManager() # if we get a stripe_token, create a new stripe account From 5bde0da8caf352254e44f070d0c283ea099e8fb4 Mon Sep 17 00:00:00 2001 From: eric Date: Sat, 13 Oct 2012 13:45:46 -0400 Subject: [PATCH 4/5] [#37716025] reuse a user's credit card --- core/models.py | 9 ++++ frontend/templates/fund_the_pledge.html | 14 +++++- frontend/templates/pledge_card_error.html | 29 ++++++++++++ frontend/views.py | 50 +++++++------------- payment/manager.py | 10 ---- payment/stripelib.py | 56 ++++++++++------------- 6 files changed, 93 insertions(+), 75 deletions(-) create mode 100644 frontend/templates/pledge_card_error.html diff --git a/core/models.py b/core/models.py index 1d7a4c8b..2ea6b8af 100755 --- a/core/models.py +++ b/core/models.py @@ -1008,6 +1008,15 @@ class UserProfile(models.Model): def pledge_count(self): return self.user.transaction_set.exclude(status='NONE').exclude(status='Canceled',reason=None).exclude(anonymous=True).count() + @property + def account(self): + # there should be only one active account per user + accounts = self.user.account_set.filter(date_deactivated__isnull=True) + if accounts.count()==0: + return None + else: + return accounts[0] + #class CampaignSurveyResponse(models.Model): # # generic # campaign = models.ForeignKey("Campaign", related_name="surveyresponse", null=False) diff --git a/frontend/templates/fund_the_pledge.html b/frontend/templates/fund_the_pledge.html index c2fa5696..397a3217 100644 --- a/frontend/templates/fund_the_pledge.html +++ b/frontend/templates/fund_the_pledge.html @@ -193,7 +193,18 @@ $j().ready(function() { {% endif %}

Pledge by Credit Card

-

Unglue.it uses Stripe to securely manage your Credit Card information. + {% if request.user.profile.account %} +

Unglue.it has a {{ request.user.profile.account.card_type }} credit card on file for you (we use Stripe to keep your information secure). + The last four digits of the card are {{ request.user.profile.account.card_last4 }}. +

+
+ {% csrf_token %} + {{ form.non_field_errors }} + {{ form.as_p }} + +
+ {% else %} +

Unglue.it uses Stripe to securely manage your credit card information.

@@ -252,6 +263,7 @@ $j().ready(function() {
+ {% endif %}
diff --git a/frontend/templates/pledge_card_error.html b/frontend/templates/pledge_card_error.html new file mode 100644 index 00000000..3d338aa7 --- /dev/null +++ b/frontend/templates/pledge_card_error.html @@ -0,0 +1,29 @@ +{% extends "basepledge.html" %} +{% load humanize %} + +{% block title %}Credit card processing Error{% endblock %} + +{% block extra_extra_head %} + + + +{% endblock %} + +{% block doccontent %} +
+ +
+
+ +

Error: Card authorization for a pledge transaction

+
+

Unglue.it would like to process your pledge, but there's some problem with our credit card processeng. We'd appreciate it if you'd contact unglue.it support. +

+
+
+ +
+
+{% endblock %} + + diff --git a/frontend/views.py b/frontend/views.py index ada67560..2c3da6ab 100755 --- a/frontend/views.py +++ b/frontend/views.py @@ -728,7 +728,7 @@ class FundPledgeView(FormView): assert self.request.user.is_authenticated() if self.transaction is None: self.transaction = get_object_or_404(Transaction, id=self.kwargs["t_id"]) - + if kwargs.has_key('data'): data = kwargs['data'].copy() else: @@ -765,53 +765,37 @@ class FundPledgeView(FormView): def form_valid(self, form): """ note desire to pledge; make sure there is a credit card to charge""" - - # first pass -- we have a token -- also do more direct coupling to stripelib -- then move to - # abstraction of payment.manager / payment.baseprocessor - - # we should getting a stripe_token only if we had asked for CC data - - # BUGBUG -- don't know whether transaction.host should be None -- but if it is, set to the - # default processor - - transaction = self.transaction - if transaction.host is None or transaction.host == PAYMENT_HOST_NONE: - transaction.host = settings.PAYMENT_PROCESSOR - - stripe_token = form.cleaned_data["stripe_token"] - preapproval_amount = form.cleaned_data["preapproval_amount"] - - logger.info('stripe_token:{0}, preapproval_amount:{1}'.format(stripe_token, preapproval_amount)) if self.transaction.user.id != self.request.user.id: # trouble! return render(request, "pledge_user_error.html", {'transaction': self.transaction }) p = PaymentManager() - - # if we get a stripe_token, create a new stripe account - - try: - (accounts, created) = p.retrieve_or_make_accounts(user=transaction.user, host=transaction.host, token=stripe_token) - assert len(accounts) == 1 # only one active account/user at the moment - account = accounts[0] - logger.info('account.id: {0}'.format(account.id)) - except baseprocessor.ProcessorError as e: - return HttpResponse("baseprocessor.ProcessorError: {0}".format(e)) - - # GOAL: deactivate any older accounts associated with user + + # if the user has active account, use it. Otherwise... + if not self.request.user.profile.account: + stripe_token = form.cleaned_data["stripe_token"] + # if we get a stripe_token, create a new stripe account for the user + if stripe_token: + try: + p.make_account(user=self.request.user, host=settings.PAYMENT_PROCESSOR, token=stripe_token) + except baseprocessor.ProcessorError as e: + return HttpResponse("There was an error processing your credit card") + + self.transaction.host = settings.PAYMENT_PROCESSOR + + preapproval_amount = form.cleaned_data["preapproval_amount"] # with the Account in hand, now authorize transaction - t, url = p.authorize(transaction) self.transaction.max_amount = preapproval_amount + t, url = p.authorize(self.transaction) logger.info("t, url: {0} {1}".format(t, url)) # redirecting user to pledge_complete on successful preapproval (in the case of stripe) - # BUGBUG: Make sure we are testing properly for successful authorization properly here if url is not None: return HttpResponseRedirect(url) else: - return HttpResponse("preapproval_key: {0}".format(transaction.preapproval_key)) + return render(request, "pledge_card_error.html", {'transaction': self.transaction }) class NonprofitCampaign(FormView): diff --git a/payment/manager.py b/payment/manager.py index b1b6e0fd..dec2022a 100644 --- a/payment/manager.py +++ b/payment/manager.py @@ -953,16 +953,6 @@ class PaymentManager( object ): else: return Account.objects.filter(user=user, host=host, date_deactivated__isnull=True) - def retrieve_or_make_accounts(self, user, host, token=None, include_deactivated=False): - accounts = self.retrieve_accounts(user=user, host=host, include_deactivated=include_deactivated) - num_acct=len(accounts) - if num_acct == 1: - return (accounts, False) - elif num_acct > 1: - return ((accounts[num_acct-1],), False) - else: - account = self.make_account(user=user, host=host, token=token) - return ((account,), True) diff --git a/payment/stripelib.py b/payment/stripelib.py index 8e55ebdd..9685d820 100644 --- a/payment/stripelib.py +++ b/payment/stripelib.py @@ -448,35 +448,31 @@ class Processor(baseprocessor.Processor): def make_account(self, user, token=None): """returns a payment.models.Account based on stripe token and user""" + if token == None: + # shouldn't happen + return None + sc = StripeClient() - # allow for the possibility that if token is None, just create a placeholder Account - - if token is not None: - - # create customer and charge id and then charge the customer - try: - customer = sc.create_customer(card=token, description=user.username, - email=user.email) - except stripe.StripeError as e: - raise StripelibError(e.message, e) - - account = Account(host = PAYMENT_HOST_STRIPE, - account_id = customer.id, - card_last4 = customer.active_card.last4, - card_type = customer.active_card.type, - card_exp_month = customer.active_card.exp_month, - card_exp_year = customer.active_card.exp_year, - card_fingerprint = customer.active_card.fingerprint, - card_country = customer.active_card.country, - user = user - ) - - account.save() - - else: - account = Account(host = PAYMENT_HOST_STRIPE, user= user) - account.save() + # create customer and charge id and then charge the customer + try: + customer = sc.create_customer(card=token, description=user.username, + email=user.email) + except stripe.StripeError as e: + raise StripelibError(e.message, e) + + account = Account(host = PAYMENT_HOST_STRIPE, + account_id = customer.id, + card_last4 = customer.active_card.last4, + card_type = customer.active_card.type, + card_exp_month = customer.active_card.exp_month, + card_exp_year = customer.active_card.exp_year, + card_fingerprint = customer.active_card.fingerprint, + card_country = customer.active_card.country, + user = user + ) + + account.save() return account @@ -507,13 +503,11 @@ class Processor(baseprocessor.Processor): # ASSUMPTION: a user has any given moment one and only one active payment Account - if transaction.user.account_set.filter(date_deactivated__isnull=True).count() > 1: - logger.warning("user {0} has more than one active payment account".format(transaction.user)) - elif transaction.user.account_set.filter(date_deactivated__isnull=True).count() == 0: + account = transaction.user.profile.account + if not account: logger.warning("user {0} has no active payment account".format(transaction.user)) raise StripelibError("user {0} has no active payment account".format(transaction.user)) - account = transaction.user.account_set.filter(date_deactivated__isnull=True)[0] logger.info("user: {0} customer.id is {1}".format(transaction.user, account.account_id)) # settings to apply to transaction for TRANSACTION_STATUS_ACTIVE From 8d6cc6f4c9d2b0b3a015bf18438cb96a73819d34 Mon Sep 17 00:00:00 2001 From: eric Date: Sat, 13 Oct 2012 14:12:34 -0400 Subject: [PATCH 5/5] this makes the boder of the preapproval amount white --- frontend/templates/fund_the_pledge.html | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/frontend/templates/fund_the_pledge.html b/frontend/templates/fund_the_pledge.html index 397a3217..fd4becdd 100644 --- a/frontend/templates/fund_the_pledge.html +++ b/frontend/templates/fund_the_pledge.html @@ -198,10 +198,12 @@ $j().ready(function() { The last four digits of the card are {{ request.user.profile.account.card_last4 }}.

- {% csrf_token %} - {{ form.non_field_errors }} - {{ form.as_p }} - +
+ {% csrf_token %} + {{ form.non_field_errors }} + {{ form.as_p }} + +
{% else %}

Unglue.it uses Stripe to securely manage your credit card information.