Implemented basic transaction_failed signal and notices -- THEY STILL NEED WORK
Tests handle situation of transaction_failed toopull/1/head
parent
3da9ce6734
commit
793d984ba3
|
@ -14,7 +14,7 @@ from social_auth.signals import pre_update
|
||||||
from social_auth.backends.facebook import FacebookBackend
|
from social_auth.backends.facebook import FacebookBackend
|
||||||
from tastypie.models import create_api_key
|
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 registration.signals
|
||||||
import django.dispatch
|
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_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_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_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_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("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."))
|
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)
|
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):
|
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
|
# 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;
|
# transactions in different ways, resulting in different user-visible behavior;
|
||||||
|
|
|
@ -11,7 +11,7 @@ from django.core import mail
|
||||||
from regluit.core.models import Work, Campaign, RightsHolder, Claim
|
from regluit.core.models import Work, Campaign, RightsHolder, Claim
|
||||||
from regluit.payment.models import Transaction
|
from regluit.payment.models import Transaction
|
||||||
from regluit.payment.manager import PaymentManager
|
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 decimal import Decimal as D
|
||||||
from regluit.utils.localdatetime import now
|
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")
|
r = self.client.post(ipn_url, data='{"id": "evt_XXXXXXXXX"}', content_type="application/json; charset=utf-8")
|
||||||
self.assertEqual(r.status_code, 400)
|
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?
|
# 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
|
# 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))
|
||||||
|
|
||||||
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)
|
self.assertEqual(r.status_code, 200)
|
||||||
|
|
||||||
# submit to pledge page
|
# submit to pledge page
|
||||||
r = self.client.post("/pledge/%s/" % self.work.id, data={'preapproval_amount':'10',
|
r = self.client.post("/pledge/%s/" % self.work.id, data={'preapproval_amount': preapproval_amount,
|
||||||
'premium_id':'150'}, follow=True)
|
'premium_id':premium_id}, follow=True)
|
||||||
self.assertEqual(r.status_code, 200)
|
self.assertEqual(r.status_code, 200)
|
||||||
|
|
||||||
# Should now be on the fund page
|
# Should now be on the fund page
|
||||||
|
@ -244,9 +244,7 @@ class UnifiedCampaignTests(TestCase):
|
||||||
time0 = time.time()
|
time0 = time.time()
|
||||||
|
|
||||||
sc = StripeClient()
|
sc = StripeClient()
|
||||||
card1 = card(number=TEST_CARDS[0][0], exp_month=1, exp_year='2020', cvc='123', name='Raymond Yee',
|
stripe_token = sc.create_token(card=card)
|
||||||
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)
|
|
||||||
r = self.client.post(pledge_fund_path, data={'stripe_token':stripe_token.id}, follow=True)
|
r = self.client.post(pledge_fund_path, data={'stripe_token':stripe_token.id}, follow=True)
|
||||||
|
|
||||||
# where are we now?
|
# where are we now?
|
||||||
|
@ -256,13 +254,33 @@ class UnifiedCampaignTests(TestCase):
|
||||||
# dig up the transaction and charge it
|
# dig up the transaction and charge it
|
||||||
pm = PaymentManager()
|
pm = PaymentManager()
|
||||||
transaction = Transaction.objects.get(id=t_id)
|
transaction = Transaction.objects.get(id=t_id)
|
||||||
|
|
||||||
|
# catch any exception and pass it along
|
||||||
|
try:
|
||||||
self.assertTrue(pm.execute_transaction(transaction, ()))
|
self.assertTrue(pm.execute_transaction(transaction, ()))
|
||||||
|
except Exception, charge_exception:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
charge_exception = None
|
||||||
|
|
||||||
time1 = time.time()
|
time1 = time.time()
|
||||||
|
|
||||||
# retrieve events from this period -- need to pass in ints for event creation times
|
# 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)}))
|
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)
|
# 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.
|
# 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")
|
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)
|
self.assertEqual(r.status_code, 200)
|
||||||
|
|
||||||
# confirm that an email was sent out for the customer.created event
|
def bad_cc_scenario(self):
|
||||||
|
"""Goal of this scenario: enter a CC that will cause a charge.failed event, have user repledge succesfully"""
|
||||||
# Test that one message has been sent.
|
|
||||||
# for initial pledging, for successful campaign, for Customer.created event
|
|
||||||
self.assertEqual(len(mail.outbox), 3)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,10 @@ from notification import models as notification
|
||||||
from django.dispatch import Signal
|
from django.dispatch import Signal
|
||||||
|
|
||||||
transaction_charged = Signal(providing_args=["transaction"])
|
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_created = Signal(providing_args=["transaction"])
|
||||||
pledge_modified = Signal(providing_args=["transaction", "up_or_down"])
|
pledge_modified = Signal(providing_args=["transaction", "up_or_down"])
|
||||||
credit_balance_added = Signal(providing_args=["amount"])
|
credit_balance_added = Signal(providing_args=["amount"])
|
||||||
|
|
|
@ -16,6 +16,8 @@ from regluit.payment.models import Account, Transaction
|
||||||
from regluit.payment.parameters import PAYMENT_HOST_STRIPE
|
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.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 import baseprocessor
|
||||||
|
from regluit.payment.signals import transaction_charged, transaction_failed
|
||||||
|
|
||||||
from regluit.utils.localdatetime import now, zuluformat
|
from regluit.utils.localdatetime import now, zuluformat
|
||||||
|
|
||||||
import stripe
|
import stripe
|
||||||
|
@ -391,7 +393,7 @@ class StripeErrorTest(TestCase):
|
||||||
self.fail("Attempt to create customer did not throw expected exception.")
|
self.fail("Attempt to create customer did not throw expected exception.")
|
||||||
except stripe.CardError as e:
|
except stripe.CardError as e:
|
||||||
self.assertEqual(e.code, "card_declined")
|
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):
|
def test_charge_bad_cust(self):
|
||||||
# expect the card to be declined -- and for us to get CardError
|
# expect the card to be declined -- and for us to get CardError
|
||||||
|
@ -747,7 +749,6 @@ class Processor(baseprocessor.Processor):
|
||||||
elif resource == 'charge':
|
elif resource == 'charge':
|
||||||
# we need to handle: succeeded, failed, refunded, disputed
|
# we need to handle: succeeded, failed, refunded, disputed
|
||||||
if action == 'succeeded':
|
if action == 'succeeded':
|
||||||
from regluit.payment.signals import transaction_charged
|
|
||||||
logger.info("charge.succeeded webhook for {0}".format(ev_object.get("id")))
|
logger.info("charge.succeeded webhook for {0}".format(ev_object.get("id")))
|
||||||
# try to parse description of object to pull related transaction if any
|
# 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
|
# 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)
|
logger.warning(e)
|
||||||
|
|
||||||
elif action == 'failed':
|
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':
|
elif action == 'refunded':
|
||||||
pass
|
pass
|
||||||
elif action == 'disputed':
|
elif action == 'disputed':
|
||||||
|
|
Loading…
Reference in New Issue