diff --git a/core/models.py b/core/models.py
index 010c9e27..91fe8e85 100755
--- a/core/models.py
+++ b/core/models.py
@@ -18,7 +18,10 @@ import regluit.core.isbn
from regluit.core.signals import successful_campaign, unsuccessful_campaign
import binascii
-from regluit.payment.parameters import TRANSACTION_STATUS_ACTIVE
+from regluit.payment.parameters import TRANSACTION_STATUS_ACTIVE, TRANSACTION_STATUS_COMPLETE, TRANSACTION_STATUS_CANCELED, TRANSACTION_STATUS_ERROR, TRANSACTION_STATUS_FAILED, TRANSACTION_STATUS_INCOMPLETE
+
+from django.db.models import Q
+
class UnglueitError(RuntimeError):
pass
@@ -323,7 +326,26 @@ class Campaign(models.Model):
def supporters_count(self):
# avoid transmitting the whole list if you don't need to; let the db do the count.
return self.transactions().filter(status=TRANSACTION_STATUS_ACTIVE).values_list('user', flat=True).distinct().count()
+
+ def transaction_to_recharge(self, user):
+ """given a user, return the transaction to be recharged if there is one -- None otherwise"""
+ # only if a campaign is SUCCESSFUL, we allow for recharged
+
+ if self.status == 'SUCCESSFUL':
+ if self.transaction_set.filter(Q(user=user) & (Q(status=TRANSACTION_STATUS_COMPLETE) | Q(status=TRANSACTION_STATUS_ACTIVE))).count():
+ # presence of an active or complete transaction means no transaction to recharge
+ return None
+ else:
+ transactions = self.transaction_set.filter(Q(user=user) & (Q(status=TRANSACTION_STATUS_ERROR) | Q(status=TRANSACTION_STATUS_FAILED)))
+ # assumption --that the first failed/errored transaction has the amount we need to recharge
+ if transactions.count():
+ return transactions[0]
+ else:
+ return None
+ else:
+ return None
+
def ungluers(self):
p = PaymentManager()
ungluers={"all":[],"supporters":[], "patrons":[], "bibliophiles":[]}
@@ -804,7 +826,21 @@ class UserProfile(models.Model):
goodreads_auth_token = models.TextField(null=True, blank=True)
goodreads_auth_secret = models.TextField(null=True, blank=True)
goodreads_user_link = models.CharField(max_length=200, null=True, blank=True)
-
+
+#class CampaignSurveyResponse(models.Model):
+# # generic
+# campaign = models.ForeignKey("Campaign", related_name="surveyresponse", null=False)
+# user = models.OneToOneField(User, related_name='surveyresponse')
+# transaction = models.ForeignKey("payment.Transaction", null=True)
+# # for OLA only
+# premium = models.ForeignKey("Premium", null=True)
+# anonymous = models.BooleanField(null=False)
+# # relevant to all campaigns since these arise from acknowledgement requirements from generic premiums
+# name = models.CharField(max_length=140, blank=True)
+# url = models.URLField(blank=True)
+# tagline = models.CharField(max_length=140, blank=True)
+# # do we need to collect address for Rupert or will he do that once he has emails?
+
# this was causing a circular import problem and we do not seem to be using
# anything from regluit.core.signals after this line
# from regluit.core import signals
diff --git a/fabfile.py b/fabfile.py
index 5f7bd7cb..91f61cc1 100644
--- a/fabfile.py
+++ b/fabfile.py
@@ -87,7 +87,9 @@ def public_key_from_private_key():
def email_addresses():
"""list email addresses in unglue.it"""
with cd("/opt/regluit"):
- run("""source ENV/bin/activate; echo "import django; print ' \\n'.join([u.email for u in django.contrib.auth.models.User.objects.all() ]); quit()" | django-admin.py shell_plus --settings=regluit.settings.me""")
+ run("""source ENV/bin/activate; echo "import django; print ' \\n'.join([u.email for u in django.contrib.auth.models.User.objects.all() ]); quit()" | django-admin.py shell_plus --settings=regluit.settings.me > /home/ubuntu/emails.txt""")
+ local("scp web1:/home/ubuntu/emails.txt .")
+ run("""rm /home/ubuntu/emails.txt""")
def selenium():
"""setting up selenium to run in the background on RY's laptop"""
diff --git a/frontend/templates/base.html b/frontend/templates/base.html
index 31498d41..61f6d1dd 100644
--- a/frontend/templates/base.html
+++ b/frontend/templates/base.html
@@ -85,7 +85,7 @@
{% block topsection %}{% endblock %}
{% block content %}{% endblock %}
diff --git a/frontend/urls.py b/frontend/urls.py
index 73b5766e..05bcd120 100644
--- a/frontend/urls.py
+++ b/frontend/urls.py
@@ -7,7 +7,7 @@ from django.conf import settings
from regluit.core.feeds import SupporterWishlistFeed
from regluit.core.models import Campaign
-from regluit.frontend.views import GoodreadsDisplayView, LibraryThingView, PledgeView, PledgeCompleteView, PledgeModifyView, PledgeCancelView, PledgeNeverMindView, FAQView
+from regluit.frontend.views import GoodreadsDisplayView, LibraryThingView, PledgeView, PledgeCompleteView, PledgeModifyView, PledgeCancelView, PledgeNeverMindView, PledgeRechargeView, FAQView
from regluit.frontend.views import CampaignListView, DonateView, WorkListView, UngluedListView, InfoPageView
urlpatterns = patterns(
@@ -55,6 +55,7 @@ urlpatterns = patterns(
url(r"^pledge/complete/$", login_required(PledgeCompleteView.as_view()), name="pledge_complete"),
url(r"^pledge/nevermind/$", login_required(PledgeNeverMindView.as_view()), name="pledge_nevermind"),
url(r"^pledge/modify/(?P\d+)$", login_required(PledgeModifyView.as_view()), name="pledge_modify"),
+ url(r"^pledge/recharge/(?P\d+)$", login_required(PledgeRechargeView.as_view()), name="pledge_recharge"),
url(r"^subjects/$", "subjects", name="subjects"),
url(r"^librarything/$", LibraryThingView.as_view(), name="librarything"),
url(r"^librarything/load/$","librarything_load", name="librarything_load"),
diff --git a/frontend/views.py b/frontend/views.py
index 5e707ad5..64da339e 100755
--- a/frontend/views.py
+++ b/frontend/views.py
@@ -674,7 +674,8 @@ class PledgeView(FormView):
class PledgeModifyView(FormView):
"""
A view to handle request to change an existing pledge
- """
+ """
+
template_name="pledge.html"
form_class = CampaignPledgeForm
embedded = False
@@ -798,6 +799,58 @@ class PledgeModifyView(FormView):
return HttpResponse("No modification made")
+
+class PledgeRechargeView(TemplateView):
+ """
+ a view to allow for recharge of a transaction for failed transactions or ones with errors
+ """
+ template_name="pledge_recharge.html"
+
+ def get_context_data(self, **kwargs):
+
+ context = super(PledgeRechargeView, self).get_context_data(**kwargs)
+
+ # the following should be true since PledgeModifyView.as_view is wrapped in login_required
+ assert self.request.user.is_authenticated()
+ user = self.request.user
+
+ work = get_object_or_404(models.Work, id=self.kwargs["work_id"])
+ campaign = work.last_campaign()
+
+ if campaign is None:
+ return Http404
+
+ transaction = campaign.transaction_to_recharge(user)
+
+ # calculate a URL to do a preapproval -- in the future, we may want to do a straight up payment
+
+ return_url = None
+ nevermind_url = None
+
+ if transaction is not None:
+ # the recipients of this authorization is not specified here but rather by the PaymentManager.
+ # set the expiry date based on the campaign deadline
+ expiry = campaign.deadline + timedelta( days=settings.PREAPPROVAL_PERIOD_AFTER_CAMPAIGN )
+
+ paymentReason = "Unglue.it Recharge for {0}".format(campaign.name)
+
+ p = PaymentManager(embedded=False)
+ t, url = p.authorize('USD', TARGET_TYPE_CAMPAIGN, transaction.amount, expiry=expiry, campaign=campaign, list=None, user=user,
+ return_url=return_url, nevermind_url=nevermind_url, anonymous=transaction.anonymous, premium=transaction.premium,
+ paymentReason=paymentReason)
+ logger.info("Recharge url: {0}".format(url))
+ else:
+ url = None
+
+ context.update({
+ 'work':work,
+ 'transaction':transaction,
+ 'payment_processor':transaction.host if transaction is not None else None,
+ 'recharge_url': url
+ })
+ return context
+
+
class PledgeCompleteView(TemplateView):
"""A callback for PayPal to tell unglue.it that a payment transaction has completed successfully.
diff --git a/payment/amazon.py b/payment/amazon.py
index 3b3dde3f..0349f075 100644
--- a/payment/amazon.py
+++ b/payment/amazon.py
@@ -220,14 +220,36 @@ def amazonPaymentReturn(request):
status = request.GET['status']
reference = request.GET['callerReference']
token = request.GET['tokenID']
-
- # BUGUBG - Should we verify the signature here?
- #
+ # validate the signature
+
+ uri = request.build_absolute_uri()
+ parsed_url = urlparse.urlparse(uri)
+
+ connection = FPSConnection(FPS_ACCESS_KEY, FPS_SECRET_KEY, host=settings.AMAZON_FPS_HOST)
+
+ # Check the validity of the IPN
+ resp = connection.verify_signature("%s://%s%s" %(parsed_url.scheme,
+ parsed_url.netloc,
+ parsed_url.path),
+ urllib.urlencode(request.GET))
+
+ if not resp[0].VerificationStatus == "Success":
+ # Error, ignore this
+ logging.error("amazonPaymentReturn cannot be verified with get data: ")
+ logging.error(request.GET)
+ return HttpResponseForbidden()
+
+ logging.debug("amazonPaymentReturn sig verified:")
+ logging.debug(request.GET)
+
+ # validation of signature ok
# Find the transaction by reference, there should only be one
- # We will catch the exception if it does not exist
- #
- transaction = Transaction.objects.get(secret=reference)
+ try:
+ transaction = Transaction.objects.get(secret=reference)
+ except:
+ logging.info("transaction with secret {0}".format(reference))
+ return HttpResponseForbidden()
logging.info("Amazon Co-branded Return URL called for transaction id: %d" % transaction.id)
logging.info(request.GET)