[#37716025] reuse a user's credit card

pull/1/head
eric 2012-10-13 13:45:46 -04:00
parent 674637f616
commit 5bde0da8ca
6 changed files with 93 additions and 75 deletions

View File

@ -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)

View File

@ -193,7 +193,18 @@ $j().ready(function() {
{% endif %}
<div id="authorize" {% if nonprofit.is_on %}class="off clearfix"{% else %}class="clearfix"{% endif %}>
<h3>Pledge by Credit Card</h3>
<p>Unglue.it uses Stripe to securely manage your Credit Card information.
{% if request.user.profile.account %}
<p>Unglue.it has a {{ request.user.profile.account.card_type }} credit card on file for you (we use <a href="https://stripe.com/">Stripe</a> to keep your information secure).
The last four digits of the card are {{ request.user.profile.account.card_last4 }}.
</p>
<form action="#" method="POST" id="use-account-form">
{% csrf_token %}
{{ form.non_field_errors }}
{{ form.as_p }}
<input id="use_account_submit" name="use_account" type="submit" class="submit-button" value="Complete Pledge" />
</form>
{% else %}
<p>Unglue.it uses <a href="https://stripe.com/">Stripe</a> to securely manage your credit card information.
</p>
<div id="cc_pledge">
<form action="" method="POST" id="payment-form">
@ -252,6 +263,7 @@ $j().ready(function() {
</form>
</div>
{% endif %}
</div>
</div>

View File

@ -0,0 +1,29 @@
{% extends "basepledge.html" %}
{% load humanize %}
{% block title %}Credit card processing Error{% endblock %}
{% block extra_extra_head %}
<link type="text/css" rel="stylesheet" href="/static/css/campaign.css" />
<link type="text/css" rel="stylesheet" href="/static/css/pledge.css" />
{% endblock %}
{% block doccontent %}
<div style="height:15px"></div>
<div class="jsmodule rounded clearfix">
<div class="jsmod-content">
<div><h2>Error: Card authorization for a pledge transaction</h2>
<div>
<p>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 <a href="mailto:support@gluejar.com?subject={{ request.get_full_path|urlencode }}">unglue.it support</a>.
</p>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -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):

View File

@ -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)

View File

@ -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