Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added campaign_name field to Donation model #335

Closed
wants to merge 12 commits into from

Conversation

olasitarska
Copy link
Contributor

No description provided.

if fixed_amount:
try:
initial = {'amount': Decimal(fixed_amount)}
initial = {'amount': Decimal(fixed_amount), 'campaign': campaign}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you mind reformatting that to multiple lines?

@jezdez
Copy link
Contributor

jezdez commented Jan 21, 2015

LGTM

Of course if we want to store more info about campaigns than just the name maybe it'd be better to have a separate campaign data model and have an optional foreign key in the donation model. That allows us to have in theory easy metadata storage and is future proof. E.g. imagine having a campaign page with just the heart for that campaign. In fact you could consider this fundraiser the first campaign we could use this system for.

@@ -160,6 +161,11 @@ class PaymentForm(forms.Form):
),
)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no newline between fields (you can remove the one above expires please)

@timgraham
Copy link
Member

The complexity of code without tests is starting to make me nervous. Would you like to write some? :tests: <-- where is a good icon when I need it?

@jezdez
Copy link
Contributor

jezdez commented Jan 21, 2015

There is is: 🐐

@jezdez
Copy link
Contributor

jezdez commented Jan 21, 2015

Reference (et al): http://www.obeythetestinggoat.com/

)
return redirect(donation)
else:
if 'amount' in form.errors:
show_amount = True
else:
fixed_amount = request.GET.get('amount') or None
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't do that, it will break with empty GET parameters.

@olasitarska
Copy link
Contributor Author

I pushed some tests here.. the submitting donation form test doesn't work yet, mocking is not really working as it should (maybe I used wrong url?). I am too tired to work on this longer today though. Not sure about tomorrow yet. Sorry!


response = self.client.post(reverse('fundraising:donate'), {
'amount': 100,
'number': '4242424242424242',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The number, cvc and expires data is not submitted to our form view, the form actually only submits the stripe_token that is set by our JavaScript. Card data never hits our servers :) So simply set a stripe_token to something and it should be valid.

@olasitarska
Copy link
Contributor Author

I fixed all the things you commented on, thanks!

I am still not able to mock Stripe requests despite mocking all of the urls. Looking at this, there is definitely more magic involved than just trying to mock the request with request_mock, I think.

@olasitarska
Copy link
Contributor Author

Uh finally! For some reason mocking API calls via setUpClass didn't work at all. It only worked when I added the mocking decorator directly on the test. Anyway, then together with @bmispelon we've decided that mocking API calls isn't really a good idea, because Stripe wanted us to return a very particular response. So these last tests I added are patching Stripe methods and it works.

Is this all ok?

* stripe calls in form.make_donation method instead
of donate view

* tests of donate view

* tests of make_donation

* logging if creation of stripe Customer failed
@asendecka
Copy link
Contributor

Meanwhile I and @oinopion took a look and refactor donate method a little bit. Previous state was pretty hard to test. We moved stripe & creating of a donation to form method. We also added logging, since there was only raise there. For now we simply use Bad Request. I think it should be addressed when solving #342 (maybe displaying form again?), but it's better than 500 anyway.

@jezdez
Copy link
Contributor

jezdez commented Jan 23, 2015

@asendecka The reason why I fell back to just catching StripeError was simply time constraints on the implementation (I did that in one afternoon after dabbling with the various Django apps that didn't do what we want), but I know there are more than just this one exception. A bad request is only better for the user, but letting the exception bubble up allowed us to see what happened in Sentry, which we wouldn't with the 400. Logging doesn't help as much here since we don't have a sensible logging setup.

Check out CardError (https://github.com/stripe/stripe-python/blob/846ec9080a7f3cd55113871803eb9d7c29e869d7/stripe/error.py#L29-L36) that has a status code attached with the reason why the card was declined: https://stripe.com/docs/api/python#errors

There is also a pretty awesome example for how to catch all the other exceptions in Python below that list errors in the Stripe docs, under "Handling errors".

try:
  # Use Stripe's bindings...
  pass
except stripe.error.CardError, e:
  # Since it's a decline, stripe.error.CardError will be caught
  body = e.json_body
  err  = body['error']

  print "Status is: %s" % e.http_status
  print "Type is: %s" % err['type']
  print "Code is: %s" % err['code']
  # param is '' in this case
  print "Param is: %s" % err['param']
  print "Message is: %s" % err['message']
except stripe.error.InvalidRequestError, e:
  # Invalid parameters were supplied to Stripe's API
  pass
except stripe.error.AuthenticationError, e:
  # Authentication with Stripe's API failed
  # (maybe you changed API keys recently)
  pass
except stripe.error.APIConnectionError, e:
  # Network communication with Stripe failed
  pass
except stripe.error.StripeError, e:
  # Display a very generic error to the user, and maybe send
  # yourself an email
  pass
except Exception, e:
  # Something else happened, completely unrelated to Stripe
  pass 

I think in case a CardError is raised we should reraise it as a ValidationError with saying something like "We're sorry but we had problems charging your card: %s" % errror_code. InvalidRequestError and AuthenticationError should raise a ImproperlyConfigured exception (with a 500 so it shows up in Sentry) since that's an error on our side, APIConnectionError probably should just redisplay the form and ask the user to try again. And then the raw StripeError and Exception should just be raised to bubble up in Sentry.

Now that you've already started the refactor, let's merge #342 into this and refactor it completely.

@olasitarska
Copy link
Contributor Author

Are going to be able to merge this small feature this pull request was about before this campaign ends?

@jezdez
Copy link
Contributor

jezdez commented Jan 24, 2015

Merged in 9e9d176.

@jezdez jezdez closed this Jan 24, 2015
@jezdez jezdez deleted the fundraising-campaign-link branch January 25, 2015 14:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants