Bruce Kroeze avatar Bruce Kroeze committed b21f089

changing paypal to use a better form

Comments (0)

Files changed (6)

bursar/gateway/paypal_gateway/forms.py

 from django import forms
 from django.utils.translation import ugettext as _
 
+
 class IpnTestForm(forms.Form):
     invoice = forms.CharField(label=_('Purchase #'), required=True, max_length=20)
     amount = forms.DecimalField(label=_('Amount'), initial=Decimal('0.00'))
     transaction = forms.CharField(label=_('Transaction ID'), required=True, max_length=45)
     note = forms.CharField(_("Note"), required=False)
-    
-    
+

bursar/gateway/paypal_gateway/processor.py

 
 """
 
-from bursar.errors import GatewayError
 from bursar.gateway.base import HeadlessPaymentProcessor
 from bursar.models import Payment, Purchase
 from django.contrib.sites.models import Site
 from django.core import urlresolvers
 from django.utils.http import urlencode
+from django.utils.safestring import mark_safe
 from django.utils.translation import ugettext as _
+
 import urllib2
 
+PAYMENT_CMD = {
+    'BUY_NOW' : '_xclick',
+    'CART' : '_cart',
+    'SUBSCRIPTION' : '_xclick-subscriptions_'
+}
+
+NO_SHIPPING = {
+    'NO' : '1',
+    'YES' : '0'
+}
+
+NO_NOTE = {
+    'NO' : "1",
+    'YES' : "0"
+}
+
+RECURRING_PAYMENT = {
+    'YES' : "1",
+    'NO' : "0"
+}
+
+
 class PaymentProcessor(HeadlessPaymentProcessor):
     """Paypal payment processor"""
 
             # use SSL for checkout
             'SSL' : False,
             
+            'LOCALE' : 'US',
+            
+            # Reattempt on fail
+            'REATTEMPT' : True,
+            
             'LABEL' : _('PayPal'),
             'EXTRA_LOGGING' : False,
             }
         self.log_extra("HTTP code %s, response text: '%s'" % (fo.code, ret))
         return False
 
-
     @property
     def ipn_url(self):
         prefix = "http"
         url = base + '/' + view_url
         self.log.debug('IPN URL=%s', url)
         return url
-        
+
+
     def submit_url(self):
         if self.is_live():
             url = self.settings['POST_URL']
         return url
 
 
-    def prepare_submit_form(self, purchase):
-        """Get a dictionary of information needed to submit to PayPal.
-        """
+    def form(self, purchase):
+        """Render a form for submission to PayPal"""
         live = self.is_live()
         pp = self.settings
         if live:
             account = pp['BUSINESS_TEST']
 
         address = pp['RETURN_ADDRESS']
+                
+        submit = {
+            'business' : account,
+            'currency_code' : pp['CURRENCY_CODE'],
+            'return' : address,
+            'notify_url' : self.ipn_url
+        }
+        
+        #TODO: eventually need to work out the terrible PayPal shipping stuff
+        #      for now, we are saying "no shipping" and adding all shipping as
+        #      a handling charge.
+        submit['no_shipping'] = NO_SHIPPING['YES']
+        submit['handling_cart'] = purchase.shipping
+        submit['tax_cart'] = purchase.tax
+        
+        # Locale
+        submit['lc'] = self.settings['LOCALE']
+        submit['invoice'] = purchase.id
+        
+        recuritems = purchase.recurring_lineitems()
+        if len(recuritems) > 1:
+            self.log.warn("Cannot have more than one subscription in one order for paypal.  Only processing the first one for %s", purchase)
 
-        recurring = None
-        for item in purchase.lineitems.all():
-            if item.is_recurring:
-                recur = item.recurdetails
-                detail = {
-                    'product': item.name, 
-                    'price': recur.recurring_price,
-                    'expire_length' : recur.expire_length,
-                    'expire_unit' : recur.expire_unit,
-                    'recurring_times' : recur.recurring_times,
-                }
-                if recur.trial:
-                    detail['trial1'] = {'price' : recur.trial_price}
-                    detail['trial1']['expire_length'] = recur.trial_length
-                    detail['trial1']['expire_unit'] = recur.expire_unit
+        if len(recuritems) > 0:
+            recur = recuritems[0]
+            submit['src'] = '1'
+            submit['cmd'] = PAYMENT_CMD['SUBSCRIPTION']
+            submit['item_name'] = recur.product.name
+            submit['item_number'] = recur.product.sku
+            submit['no_note'] = NO_NOTE['YES']
+            submit['bn'] = 'PP-SubscriptionsBF'
+            
+            # initial trial
+            if recur.trial:
+                submit['a1'] = recur.trial_price
+                submit['p1'] = recur.trial_length
+                submit['t1'] = recur.expire_unit
+                
+            if recur.trial_times > 1:
+                submit['a2'] = recur.trial_price
+                submit['p2'] = recur.trial_length
+                submit['t2'] = recur.expire_unit
+                
+            # subscription price
+            submit['a3'] = recur.price
+            submit['p3'] = recur.expire_length
+            submit['t3'] = recur.expire_unit
+            submit['srt'] = recur.recurring_times
+            submit['modify'] = '1'  # new or modify subscription
+            
+            if self.settings['REATTEMPT']:
+                reattempt = '1'
+            else:
+                reattempt = '0'
+            submit['sra'] = reattempt
+            
+        else:
+            submit['cmd'] = PAYMENT_CMD['CART']
+            submit['upload'] = '1'
 
-                    if recur.trial_times > 1:
-                        if trial1 is not None:
-                            detail['trial2'] = {'price' : recur.trial_price}
-                            detail['trial2']['expire_length'] = recur.trial_length
-                            detail['trial2']['expire_unit'] = recur.expire_unit
-                            
-                if recurring:
-                    self.log.warn("Cannot have more than one subscription in one order for paypal.  Only processing the last one for %s", purchase)
-                recurring = detail
+            if purchase.partially_paid:
+                submit['item_name_1'] = "Remaining Balance for order #%(invoice)s" % {'invoice': purchase.id}
+                submit['amount_1'] = str(purchase.remaining)
+                submit['quantity_1'] = '1'
+            else:
+                ix = 1
+                for item in purchase.lineitems.all():
+                    submit['item_name_%i' % ix] = item.name
+                    submit['amount_%i' % ix] = item.unit_price
+                    submit['quantity_%i' % ix] = item.int_quantity
+                    ix += 1
+                    
+        if purchase.bill_street1:
+            submit['first_name'] = purchase.first_name
+            submit['last_name'] = purchase.last_name
+            submit['address1'] = purchase.bill_street1
+            submit['address2'] = purchase.bill_street2
+            submit['city'] = purchase.bill_city
+            submit['country'] = purchase.bill_country
+            submit['zip'] = purchase.bill_postal_code
+            submit['email'] = purchase.email
+            submit['address_override'] = '0'
+            # only U.S. abbreviations may be used here
+            if purchase.bill_country.lower() == 'us' and len(purchase.bill_state) == 2:
+                submit['state'] = purchase.bill_state
+                
+        ret = []
+        for key, val in submit.items:
+            ret.append('<input type="hidden" name="%s" value="%s"' % (key, val))
+        return mark_safe("\n".join(ret))
 
-        ipn_url = self.ipn_url
-
-        base_env = {
-            'purchase': purchase,
-            'business': account,
-            'currency_code': pp['CURRENCY_CODE'],
-            'return_address': address,
-            'invoice': purchase.id,
-            'subscription': recurring,
-            'gateway_live' : live,
-            'ipn_url' : ipn_url
-        }
-
-        return base_env
-
-

bursar/gateway/paypal_gateway/templates/bursar/gateway/paypal_gateway/_paypal_submit_form.html

-{# Template used by paypal_sumbit_form template tag #}
-<input type="hidden" name="business" value="{{ business }}" />
-<input type="hidden" name="currency_code" value="{{ currency_code }}" />
-<input type="hidden" name="return" value="{{ return_address }}" />
-{% if subscription %}
-<input type="hidden" name="cmd" value="_xclick-subscriptions">
-<input type="hidden" name="item_name" value="{{ subscription.product.name }}">
-<input type="hidden" name="item_number" value="{{ invoice }}">
-<input type="hidden" name="invoice" value="{{ invoice }}" />
-<input type="hidden" name="no_shipping" value="1">
-<input type="hidden" name="no_note" value="1">
-<input type="hidden" name="lc" value="US">
-<input type="hidden" name="bn" value="PP-SubscriptionsBF">
-{% if subscription.trial1 %}
-<input type="hidden" name="a1" value="{{ subscription.trial1.price }}">
-<input type="hidden" name="p1" value="{{ subscription.trial1.expire_length }}">
-<input type="hidden" name="t1" value="{{ subscription.trial1.expire_unit }}">
-{% endif %}
-{% if subscription.product.trial2_price %}
-<input type="hidden" name="a2" value="{{ subscription.trial2.price }}">
-<input type="hidden" name="p2" value="{{ subscription.trial2.expire_length }}">
-<input type="hidden" name="t2" value="{{ subscription.trial2.expire_unit}}">
-{% endif %}
-<input type="hidden" name="a3" value="{{ subscription.price }}">
-<input type="hidden" name="p3" value="{{ subscription.expire_length }}">
-<input type="hidden" name="t3" value="{{ subscription.expire_unit }}">
-{% if subscription.product.recurring %}
-<input type="hidden" name="src" value="1">
-{% endif %}
-<input type="hidden" name="sra" value="1">
-<input type="hidden" name="srt" value="{{ recuring.recurring_times }}">
-{% else %}
-<input type="hidden" name="cmd" value="_cart" />
-<input type="hidden" name="upload" value="1" />
-<input type="hidden" name="no_shipping" value="1" />
-<input type="hidden" name="invoice" value="{{ invoice }}" />
-{% if purchase.partially_paid %}
-<input type="hidden" name="item_name_1" value="Remaining Balance for order #{{ invoice }}" />
-<input type="hidden" name="amount_1" value="{{purchase.remaining}}" />
-<input type="hidden" name="quantity_1" value="1" />
-{% else %}
-{% for item in purchase.lineitems.all %}
-<input type="hidden" name="item_name_{{forloop.counter}}" value="{{item.name}}" />
-<input type="hidden" name="amount_{{forloop.counter}}" value="{{item.unit_price}}" />
-<input type="hidden" name="quantity_{{forloop.counter}}" value="{{item.int_quantity}}" />
-{% endfor %}
-{# LEAVE THE HANDLING CHARGE ALONE!  PAYPAL SHIPPING WILL INEVITABLY BE MESSED UP IF YOU USE THEIR SHIPPING METHODS #}
-{# To work around their bizarre system, we are telling Paypal "no shipping" and adding all shipping charges as handling #}
-<input type="hidden" name="handling_cart" value="{{purchase.shipping}}" />
-<input type="hidden" name="tax_cart" value="{{purchase.tax}}" />
-{% endif %}
-{% endif %}
-{% if purchase.bill_street1 %}
-<input type="hidden" name="first_name" value="{{purchase.first_name}}" />
-<input type="hidden" name="last_name" value="{{purchase.last_name}}" />
-<input type="hidden" name="address1" value="{{purchase.bill_street1}}" />
-<input type="hidden" name="address2" value="{{purchase.bill_street2}}" />
-<input type="hidden" name="city" value="{{purchase.bill_city}}" />
-{# only U.S. abbreviations may be used here #}{% ifequal purchase.bill_country|lower 'us' %}{% ifequal address.state|length 2 %}<input type="hidden" name="state" value="{{purchase.bill_state}}" />{% endifequal %}{% endifequal %}
-<input type="hidden" name="country" value="{{purchase.bill_country}}" />
-<input type="hidden" name="zip" value="{{purchase.bill_postal_code}}" />
-<input type="hidden" name="email" value="{{purchase.email}}" />
-<input type="hidden" name="address_override" value="0" />
-{% endif %}
-<input type="hidden" name="notify_url" value="{{ ipn_url }}" />
+{{ form }}

bursar/gateway/paypal_gateway/templatetags/paypal_tags.py

 
 @register.inclusion_tag("bursar/gateway/paypal_gateway/_paypal_submit_form.html")
 def paypal_submit_form(gateway, purchase):
-    context = gateway.prepare_submit_form(purchase)
-    return context
+    form = gateway.form(purchase)
+    return {'form' : form}
+
+paypal_submit_form.is_safe = True
 class LineItem(models.Model):
     """A single line item in a purchase.  This is optional, only needed for certain
     gateways such as Google or PayPal."""
+    sku = models.CharField(_("SKU"), max_length=255, default="1")
     purchase = models.ForeignKey(Purchase, 
         verbose_name=_("Purchase"), related_name="lineitems")
     ordering = models.PositiveIntegerField(_('Ordering'),

projects/tests/settings.py

     'payment.modules.cybersource',
     'bursar.gateway.dummy_gateway',
     'payment.modules.dummy',
-    'bursar.gateway.protx_gateway',
-    'payment.modules.protx',
+    # 'bursar.gateway.protx_gateway',
+    # 'payment.modules.protx',
     'bursar.gateway.purchaseorder_gateway',
     'payment.modules.purchaseorder',
     'bursar.gateway.paypal_gateway',
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.