NewsBlur/vendor/paypal/pro/views.py

200 lines
8.5 KiB
Python
Raw Normal View History

2010-10-16 18:59:02 -04:00
#!/usr/bin/env python
# -*- coding: utf-8 -*-
2014-11-07 16:22:19 -08:00
from django.conf import settings
from django.template import RequestContext
from django.shortcuts import render_to_response
2010-10-16 18:59:02 -04:00
from django.http import HttpResponseRedirect
from django.utils.http import urlencode
2014-11-07 16:22:19 -08:00
from paypal.pro.forms import PaymentForm, ConfirmForm
from paypal.pro.helpers import PayPalWPP
from paypal.pro.exceptions import PayPalFailure
2010-10-16 18:59:02 -04:00
# PayPal Edit IPN URL:
# https://www.sandbox.paypal.com/us/cgi-bin/webscr?cmd=_profile-ipn-notify
EXPRESS_ENDPOINT = "https://www.paypal.com/webscr?cmd=_express-checkout&%s"
SANDBOX_EXPRESS_ENDPOINT = "https://www.sandbox.paypal.com/webscr?cmd=_express-checkout&%s"
class PayPalPro(object):
"""
This class-based view takes care of PayPal WebsitePaymentsPro (WPP).
2014-11-07 16:22:19 -08:00
PayPalPro has two separate flows - DirectPayment and ExpressPayFlow. In
2010-10-16 18:59:02 -04:00
DirectPayment the user buys on your site. In ExpressPayFlow the user is
2014-11-07 16:22:19 -08:00
direct to PayPal to confirm their purchase. PayPalPro implements both
2010-10-16 18:59:02 -04:00
flows. To it create an instance using the these parameters:
item: a dictionary that holds information about the item being purchased.
2014-11-07 16:22:19 -08:00
2010-10-16 18:59:02 -04:00
For single item purchase (pay once):
2014-11-07 16:22:19 -08:00
2010-10-16 18:59:02 -04:00
Required Keys:
* amt: Float amount of the item.
2014-11-07 16:22:19 -08:00
2010-10-16 18:59:02 -04:00
Optional Keys:
* custom: You can set this to help you identify a transaction.
* invnum: Unique ID that identifies this transaction.
2014-11-07 16:22:19 -08:00
2010-10-16 18:59:02 -04:00
For recurring billing:
2014-11-07 16:22:19 -08:00
2010-10-16 18:59:02 -04:00
Required Keys:
* amt: Float amount for each billing cycle.
* billingperiod: String unit of measure for the billing cycle (Day|Week|SemiMonth|Month|Year)
* billingfrequency: Integer number of periods that make up a cycle.
* profilestartdate: The date to begin billing. "2008-08-05T17:00:00Z" UTC/GMT
* desc: Description of what you're billing for.
2014-11-07 16:22:19 -08:00
2010-10-16 18:59:02 -04:00
Optional Keys:
* trialbillingperiod: String unit of measure for trial cycle (Day|Week|SemiMonth|Month|Year)
* trialbillingfrequency: Integer # of periods in a cycle.
* trialamt: Float amount to bill for the trial period.
* trialtotalbillingcycles: Integer # of cycles for the trial payment period.
* failedinitamtaction: set to continue on failure (ContinueOnFailure / CancelOnFailure)
* maxfailedpayments: number of payments before profile is suspended.
* autobilloutamt: automatically bill outstanding amount.
* subscribername: Full name of the person who paid.
* profilereference: Unique reference or invoice number.
* taxamt: How much tax.
* initamt: Initial non-recurring payment due upon creation.
* currencycode: defaults to USD
* + a bunch of shipping fields
2014-11-07 16:22:19 -08:00
2010-10-16 18:59:02 -04:00
payment_form_cls: form class that will be used to display the payment form.
It should inherit from `paypal.pro.forms.PaymentForm` if you're adding more.
2014-11-07 16:22:19 -08:00
2010-10-16 18:59:02 -04:00
payment_template: template used to ask the dude for monies. To comply with
PayPal standards it must include a link to PayPal Express Checkout.
2014-11-07 16:22:19 -08:00
2010-10-16 18:59:02 -04:00
confirm_form_cls: form class that will be used to display the confirmation form.
It should inherit from `paypal.pro.forms.ConfirmForm`. It is only used in the Express flow.
2014-11-07 16:22:19 -08:00
2010-10-16 18:59:02 -04:00
success_url / fail_url: URLs to be redirected to when the payment successful or fails.
"""
errors = {
"processing": "There was an error processing your payment. Check your information and try again.",
"form": "Please correct the errors below and try again.",
"paypal": "There was a problem contacting PayPal. Please try again later."
}
2014-11-07 16:22:19 -08:00
2010-10-16 18:59:02 -04:00
def __init__(self, item=None, payment_form_cls=PaymentForm,
2014-11-07 16:22:19 -08:00
payment_template="pro/payment.html", confirm_form_cls=ConfirmForm,
confirm_template="pro/confirm.html", success_url="?success",
fail_url=None, context=None, form_context_name="form"):
2010-10-16 18:59:02 -04:00
self.item = item
self.payment_form_cls = payment_form_cls
self.payment_template = payment_template
self.confirm_form_cls = confirm_form_cls
self.confirm_template = confirm_template
self.success_url = success_url
self.fail_url = fail_url
self.context = context or {}
self.form_context_name = form_context_name
def __call__(self, request):
"""Return the appropriate response for the state of the transaction."""
self.request = request
if request.method == "GET":
if self.should_redirect_to_express():
return self.redirect_to_express()
elif self.should_render_confirm_form():
return self.render_confirm_form()
elif self.should_render_payment_form():
2014-11-07 16:22:19 -08:00
return self.render_payment_form()
2010-10-16 18:59:02 -04:00
else:
if self.should_validate_confirm_form():
return self.validate_confirm_form()
elif self.should_validate_payment_form():
return self.validate_payment_form()
2014-11-07 16:22:19 -08:00
2010-10-16 18:59:02 -04:00
# Default to the rendering the payment form.
return self.render_payment_form()
def is_recurring(self):
return self.item is not None and 'billingperiod' in self.item
def should_redirect_to_express(self):
return 'express' in self.request.GET
2014-11-07 16:22:19 -08:00
2010-10-16 18:59:02 -04:00
def should_render_confirm_form(self):
return 'token' in self.request.GET and 'PayerID' in self.request.GET
2014-11-07 16:22:19 -08:00
2010-10-16 18:59:02 -04:00
def should_render_payment_form(self):
return True
def should_validate_confirm_form(self):
2014-11-07 16:22:19 -08:00
return 'token' in self.request.POST and 'PayerID' in self.request.POST
2010-10-16 18:59:02 -04:00
def should_validate_payment_form(self):
return True
def render_payment_form(self):
"""Display the DirectPayment for entering payment information."""
self.context[self.form_context_name] = self.payment_form_cls()
return render_to_response(self.payment_template, self.context, RequestContext(self.request))
2010-10-16 18:59:02 -04:00
def validate_payment_form(self):
"""Try to validate and then process the DirectPayment form."""
2014-11-07 16:22:19 -08:00
form = self.payment_form_cls(self.request.POST)
2010-10-16 18:59:02 -04:00
if form.is_valid():
success = form.process(self.request, self.item)
if success:
return HttpResponseRedirect(self.success_url)
else:
self.context['errors'] = self.errors['processing']
self.context[self.form_context_name] = form
self.context.setdefault("errors", self.errors['form'])
return render_to_response(self.payment_template, self.context, RequestContext(self.request))
2010-10-16 18:59:02 -04:00
def get_endpoint(self):
2014-11-07 16:22:19 -08:00
if getattr(settings, 'PAYPAL_TEST', True):
2010-10-16 18:59:02 -04:00
return SANDBOX_EXPRESS_ENDPOINT
else:
return EXPRESS_ENDPOINT
def redirect_to_express(self):
"""
2014-11-07 16:22:19 -08:00
First step of ExpressCheckout. Redirect the request to PayPal using the
2010-10-16 18:59:02 -04:00
data returned from setExpressCheckout.
"""
wpp = PayPalWPP(self.request)
2014-11-07 16:22:19 -08:00
try:
nvp_obj = wpp.setExpressCheckout(self.item)
except PayPalFailure:
2010-10-16 18:59:02 -04:00
self.context['errors'] = self.errors['paypal']
return self.render_payment_form()
2014-11-07 16:22:19 -08:00
else:
pp_params = dict(token=nvp_obj.token)
pp_url = self.get_endpoint() % urlencode(pp_params)
return HttpResponseRedirect(pp_url)
2010-10-16 18:59:02 -04:00
def render_confirm_form(self):
"""
Second step of ExpressCheckout. Display an order confirmation form which
2014-11-07 16:22:19 -08:00
contains hidden fields with the token / PayerID from PayPal.
2010-10-16 18:59:02 -04:00
"""
initial = dict(token=self.request.GET['token'], PayerID=self.request.GET['PayerID'])
self.context[self.form_context_name] = self.confirm_form_cls(initial=initial)
return render_to_response(self.confirm_template, self.context, RequestContext(self.request))
2010-10-16 18:59:02 -04:00
def validate_confirm_form(self):
"""
Third and final step of ExpressCheckout. Request has pressed the confirmation but
and we can send the final confirmation to PayPal using the data from the POST'ed form.
"""
wpp = PayPalWPP(self.request)
pp_data = dict(token=self.request.POST['token'], payerid=self.request.POST['PayerID'])
self.item.update(pp_data)
2014-11-07 16:22:19 -08:00
# @@@ This check and call could be moved into PayPalWPP.
try:
if self.is_recurring():
wpp.createRecurringPaymentsProfile(self.item)
2014-11-07 16:22:19 -08:00
else:
wpp.doExpressCheckoutPayment(self.item)
2014-11-07 16:22:19 -08:00
except PayPalFailure:
2010-10-16 18:59:02 -04:00
self.context['errors'] = self.errors['processing']
return self.render_payment_form()
2014-11-07 16:22:19 -08:00
else:
return HttpResponseRedirect(self.success_url)