Implemented basic transaction_failed signal and notices -- THEY STILL NEED WORK

Tests handle situation of transaction_failed too
pull/1/head
Raymond Yee 2012-11-06 11:22:25 -08:00
parent 3da9ce6734
commit 793d984ba3
4 changed files with 110 additions and 25 deletions

View File

@ -14,7 +14,7 @@ from social_auth.signals import pre_update
from social_auth.backends.facebook import FacebookBackend
from tastypie.models import create_api_key
from regluit.payment.signals import transaction_charged, pledge_modified, pledge_created
from regluit.payment.signals import transaction_charged, transaction_failed, pledge_modified, pledge_created
import registration.signals
import django.dispatch
@ -94,6 +94,7 @@ def create_notice_types(app, created_models, verbosity, **kwargs):
notification.create_notice_type("pledge_you_have_pledged", _("Thanks For Your Pledge!"), _("Your ungluing pledge has been entered."))
notification.create_notice_type("pledge_status_change", _("Your Pledge Has Been Modified"), _("Your ungluing pledge has been modified."))
notification.create_notice_type("pledge_charged", _("Your Pledge has been Executed"), _("You have contributed to a successful ungluing campaign."))
notification.create_notice_type("pledge_failed", _("Unable to charge your credit card"), _("A charge to your credit card did not go through."))
notification.create_notice_type("rights_holder_created", _("Agreement Accepted"), _("You have become a verified Unglue.it rights holder."))
notification.create_notice_type("rights_holder_claim_approved", _("Claim Accepted"), _("A claim you've entered has been accepted."))
notification.create_notice_type("wishlist_unsuccessful_amazon", _("Campaign shut down"), _("An ungluing campaign that you supported had to be shut down due to an Amazon Payments policy change."))
@ -171,6 +172,21 @@ def handle_transaction_charged(sender,transaction=None, **kwargs):
transaction_charged.connect(handle_transaction_charged)
# dealing with failed transactions
def handle_transaction_failed(sender,transaction=None, **kwargs):
if transaction==None:
return
notification.queue([transaction.user], "pledge_failed", {
'site':Site.objects.get_current(),
'transaction':transaction
}, True)
from regluit.core.tasks import emit_notifications
emit_notifications.delay()
transaction_failed.connect(handle_transaction_failed)
def handle_pledge_modified(sender, transaction=None, up_or_down=None, **kwargs):
# we need to know if pledges were modified up or down because Amazon handles the
# transactions in different ways, resulting in different user-visible behavior;

View File

@ -11,7 +11,7 @@ from django.core import mail
from regluit.core.models import Work, Campaign, RightsHolder, Claim
from regluit.payment.models import Transaction
from regluit.payment.manager import PaymentManager
from regluit.payment.stripelib import StripeClient, TEST_CARDS, card
from regluit.payment.stripelib import StripeClient, TEST_CARDS, ERROR_TESTING, card
from decimal import Decimal as D
from regluit.utils.localdatetime import now
@ -210,13 +210,13 @@ class UnifiedCampaignTests(TestCase):
r = self.client.post(ipn_url, data='{"id": "evt_XXXXXXXXX"}', content_type="application/json; charset=utf-8")
self.assertEqual(r.status_code, 400)
def pledge_to_work_with_cc(self, username, password, work_id, card, preapproval_amount='10', premium_id='150'):
def test_pledge1(self):
# how much of test.campaigntest.test_relaunch can be done here?
self.assertTrue(self.client.login(username="RaymondYee", password="Test_Password_"))
self.assertTrue(self.client.login(username=username, password=password))
# Pro Web 2.0 Mashups
self.work = Work.objects.get(id=1)
self.work = Work.objects.get(id=work_id)
r = self.client.get("/work/%s/" % (self.work.id))
r = self.client.get("/work/%s/" % self.work.id)
@ -227,8 +227,8 @@ class UnifiedCampaignTests(TestCase):
self.assertEqual(r.status_code, 200)
# submit to pledge page
r = self.client.post("/pledge/%s/" % self.work.id, data={'preapproval_amount':'10',
'premium_id':'150'}, follow=True)
r = self.client.post("/pledge/%s/" % self.work.id, data={'preapproval_amount': preapproval_amount,
'premium_id':premium_id}, follow=True)
self.assertEqual(r.status_code, 200)
# Should now be on the fund page
@ -244,9 +244,7 @@ class UnifiedCampaignTests(TestCase):
time0 = time.time()
sc = StripeClient()
card1 = card(number=TEST_CARDS[0][0], exp_month=1, exp_year='2020', cvc='123', name='Raymond Yee',
address_line1="100 Jackson St.", address_line2="", address_zip="94706", address_state="CA", address_country=None) # good card
stripe_token = sc.create_token(card=card1)
stripe_token = sc.create_token(card=card)
r = self.client.post(pledge_fund_path, data={'stripe_token':stripe_token.id}, follow=True)
# where are we now?
@ -256,13 +254,33 @@ class UnifiedCampaignTests(TestCase):
# dig up the transaction and charge it
pm = PaymentManager()
transaction = Transaction.objects.get(id=t_id)
# catch any exception and pass it along
try:
self.assertTrue(pm.execute_transaction(transaction, ()))
except Exception, charge_exception:
pass
else:
charge_exception = None
time1 = time.time()
# retrieve events from this period -- need to pass in ints for event creation times
events = list(sc._all_objs('Event', created={'gte':int(time0), 'lte':int(time1+1.0)}))
return (events, charge_exception)
def good_cc_scenario(self):
# how much of test.campaigntest.test_relaunch can be done here?
card1 = card(number=TEST_CARDS[0][0], exp_month=1, exp_year='2020', cvc='123', name='Raymond Yee',
address_line1="100 Jackson St.", address_line2="", address_zip="94706", address_state="CA", address_country=None) # good card
(events, charge_exception) = self.pledge_to_work_with_cc(username="RaymondYee", password="Test_Password_", work_id=1, card=card1,
preapproval_amount='10', premium_id='150')
self.assertEqual(charge_exception, None)
# expect to have 2 events (there is a possibility that someone else could be running tests on this stripe account at the same time)
# events returned sorted in reverse chronological order.
@ -277,18 +295,51 @@ class UnifiedCampaignTests(TestCase):
r = self.client.post(ipn_url, data=json.dumps({"id": event.id}), content_type="application/json; charset=utf-8")
self.assertEqual(r.status_code, 200)
# confirm that an email was sent out for the customer.created event
# Test that one message has been sent.
# for initial pledging, for successful campaign, for Customer.created event
self.assertEqual(len(mail.outbox), 3)
def bad_cc_scenario(self):
"""Goal of this scenario: enter a CC that will cause a charge.failed event, have user repledge succesfully"""
card1 = card(number=ERROR_TESTING['BAD_ATTACHED_CARD'][0])
(events, charge_exception) = self.pledge_to_work_with_cc(username="dataunbound", password="numbers_unbound", work_id=2, card=card1,
preapproval_amount='10', premium_id='150')
# we should have an exception when the charge was attempted
self.assertTrue(charge_exception is not None)
# expect to have 2 events (there is a possibility that someone else could be running tests on this stripe account at the same time)
# events returned sorted in reverse chronological order.
self.assertEqual(len(events), 2)
self.assertEqual(events[0].type, 'charge.failed')
self.assertEqual(events[1].type, 'customer.created')
# now feed each of the events to the IPN processor.
ipn_url = reverse("HandleIPN", args=('stripelib',))
for (i, event) in enumerate(events):
r = self.client.post(ipn_url, data=json.dumps({"id": event.id}), content_type="application/json; charset=utf-8")
self.assertEqual(r.status_code, 200)
def test_good_bad_cc_scenarios(self):
self.good_cc_scenario()
self.bad_cc_scenario()
# look at emails generated through these scenarios
#print len(mail.outbox)
#for (i, m) in enumerate(mail.outbox):
# print i, m.subject
#0 [localhost:8000] Thank you for supporting Pro Web 2.0 Mashups at Unglue.it!
#1 [localhost:8000] Thanks to you, the campaign for Pro Web 2.0 Mashups has succeeded!
#2 Stripe Customer (id cus_0gf9OjXHDhBwjw; description: RaymondYee) created
#3 [localhost:8000] Thank you for supporting Moby Dick at Unglue.it!
#4 [localhost:8000] Someone new has wished for your work at Unglue.it
#5 [localhost:8000] Thanks to you, the campaign for Moby Dick has succeeded! However, your credit card charge failed.
#6 Stripe Customer (id cus_0gf93tJp036kmG; description: dataunbound) created
self.assertEqual(len(mail.outbox), 7)

View File

@ -2,6 +2,10 @@ from notification import models as notification
from django.dispatch import Signal
transaction_charged = Signal(providing_args=["transaction"])
transaction_failed = Signal(providing_args=["transaction"])
transaction_refunded = Signal(providing_args=["transaction"])
transaction_disputed = Signal(providing_args=["transaction"])
pledge_created = Signal(providing_args=["transaction"])
pledge_modified = Signal(providing_args=["transaction", "up_or_down"])
credit_balance_added = Signal(providing_args=["amount"])

View File

@ -16,6 +16,8 @@ from regluit.payment.models import Account, Transaction
from regluit.payment.parameters import PAYMENT_HOST_STRIPE
from regluit.payment.parameters import TRANSACTION_STATUS_ACTIVE, TRANSACTION_STATUS_COMPLETE, TRANSACTION_STATUS_ERROR, PAYMENT_TYPE_AUTHORIZATION, TRANSACTION_STATUS_CANCELED
from regluit.payment import baseprocessor
from regluit.payment.signals import transaction_charged, transaction_failed
from regluit.utils.localdatetime import now, zuluformat
import stripe
@ -391,7 +393,7 @@ class StripeErrorTest(TestCase):
self.fail("Attempt to create customer did not throw expected exception.")
except stripe.CardError as e:
self.assertEqual(e.code, "card_declined")
self.assertEqual(e.message, "Your card was declined.")
self.assertEqual(e.message, "Your card was declined")
def test_charge_bad_cust(self):
# expect the card to be declined -- and for us to get CardError
@ -747,7 +749,6 @@ class Processor(baseprocessor.Processor):
elif resource == 'charge':
# we need to handle: succeeded, failed, refunded, disputed
if action == 'succeeded':
from regluit.payment.signals import transaction_charged
logger.info("charge.succeeded webhook for {0}".format(ev_object.get("id")))
# try to parse description of object to pull related transaction if any
# wrapping this in a try statement because it possible that we have a charge.succeeded outside context of unglue.it
@ -766,7 +767,20 @@ class Processor(baseprocessor.Processor):
logger.warning(e)
elif action == 'failed':
pass
logger.info("charge.failed webhook for {0}".format(ev_object.get("id")))
try:
charge_meta = json.loads(ev_object["description"])
transaction = Transaction.objects.get(id=charge_meta["t.id"])
# now check that account associated with the transaction matches
# ev.data.object.id, t.pay_key
if ev_object.id == transaction.pay_key:
logger.info("ev_object.id == transaction.pay_key: {0}".format(ev_object.id))
else:
logger.warning("ev_object.id {0} <> transaction.pay_key {1}".format(ev_object.id, transaction.pay_key))
# now -- should fire off transaction_charged here -- if so we need to move it from ?
transaction_failed.send(sender=self, transaction=transaction)
except Exception, e:
logger.warning(e)
elif action == 'refunded':
pass
elif action == 'disputed':