Skip to content
Browse files

chapter 6

  • Loading branch information...
1 parent 9662dac commit 719d8b8b519adfad9868c465c28f7036e7db1ffd @ButuzGOL committed
View
33 SSLMiddleware.py
@@ -0,0 +1,33 @@
+from django.conf import settings
+from django.http import HttpResponseRedirect, \
+ HttpResponsePermanentRedirect, get_host
+
+SSL = 'SSL'
+
+class SSLRedirect:
+
+ def process_view(self, request, view_func, view_args, view_kwargs):
+ if SSL in view_kwargs:
+ secure = view_kwargs[SSL]
+ del view_kwargs[SSL]
+ else:
+ secure = False
+ if not secure == self._is_secure(request):
+ return self._redirect(request, secure)
+
+ def _is_secure(self, request):
+ if request.is_secure():
+ return True
+ if 'HTTP_X_FORWARDED_SSL' in request.META:
+ return request.META['HTTP_X_FORWARDED_SSL'] == 'on'
+ return False
+
+ def _redirect(self, request, secure):
+ protocol = secure and "https" or "http"
+ newurl = "%s://%s%s" % (protocol,get_host(request),request.get_full_path())
+ if settings.DEBUG and request.method == 'POST':
+ raise RuntimeError, \
+ """Django can't perform a SSL redirect while maintaining POST data.
+ Please structure your views so that redirects only occur during
+ GETs."""
+ return HttpResponsePermanentRedirect(newurl)
View
7 cart/cart.py
@@ -89,3 +89,10 @@ def cart_subtotal(request):
cart_total += cart_item.product.price * cart_item.quantity
return cart_total
+def is_empty(request):
+ return cart_distinct_item_count(request) == 0
+
+def empty_cart(request):
+ user_cart = get_cart_items(request)
+ user_cart.delete()
+
View
13 cart/views.py
@@ -1,17 +1,24 @@
from django.shortcuts import render_to_response
from django.template import RequestContext
from ecomstore.cart import cart
+from django.http import HttpResponseRedirect
+from ecomstore.checkout import checkout
+from ecomstore import settings
-def show_cart(request, template_name="cart/cart.html"):
+def show_cart(request, template_name):
if request.method == 'POST':
postdata = request.POST.copy()
if postdata['submit'] == 'Remove':
cart.remove_from_cart(request)
if postdata['submit'] == 'Update':
cart.update_cart(request)
+ if postdata['submit'] == 'Checkout':
+ checkout_url = checkout.get_checkout_url(request)
+ return HttpResponseRedirect(checkout_url)
cart_items = cart.get_cart_items(request)
- page_title = 'Shopping Cart'
+ page_title = 'Shopping Cart'
cart_subtotal = cart.cart_subtotal(request)
+ # for Google Checkout button
+ merchant_id = settings.GOOGLE_CHECKOUT_MERCHANT_ID
return render_to_response(template_name, locals(),
context_instance=RequestContext(request))
-
View
0 checkout/__init__.py
No changes.
View
24 checkout/admin.py
@@ -0,0 +1,24 @@
+from django.contrib import admin
+from ecomstore.checkout.models import Order, OrderItem
+
+class OrderItemInline(admin.StackedInline):
+ model = OrderItem
+ extra = 0
+
+class OrderAdmin(admin.ModelAdmin):
+ list_display = ('__unicode__', 'date', 'status', 'transaction_id', 'user')
+ list_filter = ('status', 'date')
+ search_fields = ('email', 'shipping_name', 'billing_name', 'id',
+ 'transaction_id')
+ inlines = [OrderItemInline,]
+ fieldsets = (
+ ('Basics', {'fields': ('status','email','phone')}),
+ ('Shipping', {'fields':('shipping_name','shipping_address_1',
+ 'shipping_address_2','shipping_city','shipping_state',
+ 'shipping_zip','shipping_country')}),
+ ('Billing', {'fields':('billing_name','billing_address_1',
+ 'billing_address_2','billing_city','billing_state',
+ 'billing_zip','billing_country')})
+ )
+
+admin.site.register(Order, OrderAdmin)
View
27 checkout/authnet.py
@@ -0,0 +1,27 @@
+from ecomstore import settings
+import httplib
+import urllib
+
+def do_auth_capture(amount='0.00', card_num=None, exp_date=None, card_cvv=None):
+ delimiter = '|'
+ raw_params = {
+ 'x_login':settings.AUTHNET_LOGIN,
+ 'x_tran_key':settings.AUTHNET_KEY,
+ 'x_type':'AUTH_CAPTURE',
+ 'x_amount':amount,
+ 'x_version':'3.1',
+ 'x_card_num':card_num,
+ 'x_exp_date':exp_date,
+ 'x_delim_char':delimiter,
+ 'x_relay_response':'FALSE',
+ 'x_delim_data':'TRUE',
+ 'x_card_code':card_cvv
+ }
+ params = urllib.urlencode(raw_params)
+ headers = { 'content-type':'application/x-www-form-urlencoded',
+ 'content-length':len(params) }
+ post_url = settings.AUTHNET_POST_URL
+ post_path = settings.AUTHNET_POST_PATH
+ cn = httplib.HTTPSConnection(post_url, httplib.HTTPS_PORT)
+ cn.request('POST', post_path, params, headers)
+ return cn.getresponse().read().split(delimiter)
View
66 checkout/checkout.py
@@ -0,0 +1,66 @@
+from ecomstore.checkout import google_checkout
+from ecomstore.cart import cart
+from ecomstore.checkout.models import Order, OrderItem
+from ecomstore.checkout.forms import CheckoutForm
+from ecomstore.checkout import authnet
+from ecomstore import settings
+from django.core import urlresolvers
+import urllib
+
+# returns the URL from the checkout module for cart
+def get_checkout_url(request):
+ return urlresolvers.reverse('checkout')
+
+def process(request):
+ # Transaction results
+ APPROVED = '1'
+ DECLINED = '2'
+ ERROR = '3'
+ HELD_FOR_REVIEW = '4'
+ postdata = request.POST.copy()
+ card_num = postdata.get('credit_card_number', '')
+ exp_month = postdata.get('credit_card_expire_month', '')
+ exp_year = postdata.get('credit_card_expire_year', '')
+ exp_date = exp_month + exp_year
+ cvv = postdata.get('credit_card_cvv', '')
+ amount = cart.cart_subtotal(request)
+ results = {}
+ response = authnet.do_auth_capture(amount=amount,
+ card_num=card_num,
+ exp_date=exp_date,
+ card_cvv=cvv)
+ if response[0] == APPROVED:
+ transaction_id = response[6]
+ order = create_order(request, transaction_id)
+ results = {'order_number':order.id, 'message':''}
+ if response[0] == DECLINED:
+ results = {'order_number':0,
+ 'message':'There is a problem with your credit card.'}
+ if response[0] == ERROR or response[0] == HELD_FOR_REVIEW:
+ results = {'order_number':0, 'message':'Error processing your order.'}
+ return results
+
+def create_order(request, transaction_id):
+ order = Order()
+ checkout_form = CheckoutForm(request.POST, instance=order)
+ order = checkout_form.save(commit=False)
+ order.transaction_id = transaction_id
+ order.ip_address = request.META.get('REMOTE_ADDR')
+ order.user = None
+ order.status = Order.SUBMITTED
+ order.save()
+ # if the order save succeeded
+ if order.pk:
+ cart_items = cart.get_cart_items(request)
+ for ci in cart_items:
+ # create order item for each cart item
+ oi = OrderItem()
+ oi.order = order
+ oi.quantity = ci.quantity
+ oi.price = ci.price # now using @property
+ oi.product = ci.product
+ oi.save()
+ # all set, empty cart
+ cart.empty_cart(request)
+ # return the new order object
+ return order
View
90 checkout/forms.py
@@ -0,0 +1,90 @@
+from django import forms
+from ecomstore.checkout.models import Order
+import datetime
+import re
+
+def cc_expire_years():
+ current_year = datetime.datetime.now().year
+ years = range(current_year, current_year+12)
+ return [(str(x),str(x)) for x in years]
+
+def cc_expire_months():
+ months = []
+ for month in range(1, 13):
+ if len(str(month)) == 1:
+ numeric = '0' + str(month)
+ else:
+ numeric = str(month)
+ months.append((numeric, datetime.date(2009, month, 1).strftime('%B')))
+ return months
+
+CARD_TYPES = (('Mastercard','Mastercard'),
+ ('VISA','VISA'),
+ ('AMEX','AMEX'),
+ ('Discover','Discover'),)
+
+def strip_non_numbers(data):
+ """ gets rid of all non-number characters """
+ non_numbers = re.compile('\D')
+ return non_numbers.sub('', data)
+
+# Gateway test credit cards won't pass this validation
+def cardLuhnChecksumIsValid(card_number):
+ """ checks to make sure that the card passes a luhn mod-10 checksum """
+ sum = 0
+ num_digits = len(card_number)
+ oddeven = num_digits & 1
+ for count in range(0, num_digits):
+ digit = int(card_number[count])
+ if not (( count & 1 ) ^ oddeven ):
+ digit = digit * 2
+ if digit > 9:
+ digit = digit - 9
+ sum = sum + digit
+ return ( (sum % 10) == 0 )
+
+class CheckoutForm(forms.ModelForm):
+ def __init__(self, *args, **kwargs):
+ super(CheckoutForm, self).__init__(*args, **kwargs)
+ # override default attributes
+ for field in self.fields:
+ self.fields[field].widget.attrs['size'] = '30'
+ self.fields['shipping_state'].widget.attrs['size'] = '3'
+ self.fields['shipping_state'].widget.attrs['size'] = '3'
+ self.fields['shipping_zip'].widget.attrs['size'] = '6'
+ self.fields['billing_state'].widget.attrs['size'] = '3'
+ self.fields['billing_state'].widget.attrs['size'] = '3'
+ self.fields['billing_zip'].widget.attrs['size'] = '6'
+ self.fields['credit_card_type'].widget.attrs['size'] = '1'
+ self.fields['credit_card_expire_year'].widget.attrs['size'] = '1'
+ self.fields['credit_card_expire_month'].widget.attrs['size'] = '1'
+ self.fields['credit_card_cvv'].widget.attrs['size'] = '5'
+
+ class Meta:
+ model = Order
+ exclude = ('status', 'ip_address', 'user', 'transaction_id',)
+
+ credit_card_number = forms.CharField()
+ credit_card_type = forms.CharField(widget=forms.Select(choices=CARD_TYPES))
+ credit_card_expire_month = \
+ forms.CharField(widget=forms.Select(choices=cc_expire_months()))
+ credit_card_expire_year = \
+ forms.CharField(widget=forms.Select(choices=cc_expire_years()))
+ credit_card_cvv = forms.CharField()
+
+ def clean_credit_card_number(self):
+ cc_number = self.cleaned_data['credit_card_number']
+ stripped_cc_number = strip_non_numbers(cc_number)
+ if not cardLuhnChecksumIsValid(stripped_cc_number):
+ raise forms.ValidationError('The credit card you entered is \
+ invalid.')
+
+ def clean_phone(self):
+ phone = self.cleaned_data['phone']
+ stripped_phone = strip_non_numbers(phone)
+ if len(stripped_phone) < 10:
+ raise forms.ValidationError('Enter a valid phone number with area \
+ code.(e.g.555-555-5555)')
+ return self.cleaned_data['phone']
+
+
View
93 checkout/google_checkout.py
@@ -0,0 +1,93 @@
+from xml.dom.minidom import Document
+from xml.dom import minidom
+from django.http import HttpRequest, HttpResponseRedirect
+from urllib2 import Request, urlopen, HTTPError, URLError
+import base64
+from ecomstore.cart.models import CartItem
+from ecomstore.cart import cart
+from ecomstore import settings
+
+def get_checkout_url(request):
+ redirect_url = ''
+ req =_create_google_checkout_request(request)
+ try:
+ response_xml = urlopen(req).read()
+ except HTTPError, err:
+ raise err
+ except URLError, err:
+ raise err
+ else:
+ redirect_url = _parse_google_checkout_response(response_xml)
+ return redirect_url
+
+def _create_google_checkout_request(request):
+ url = settings.GOOGLE_CHECKOUT_URL
+ cart = _build_xml_shopping_cart(request)
+ req = Request(url=url, data=cart)
+ merchant_id = settings.GOOGLE_CHECKOUT_MERCHANT_ID
+ merchant_key = settings.GOOGLE_CHECKOUT_MERCHANT_KEY
+ key_id = merchant_id + ':' + merchant_key
+ authorization_value = base64.encodestring(key_id)[:-1]
+ req.add_header('Authorization', 'Basic %s' % authorization_value)
+ req.add_header('Content-Type','application/xml; charset=UTF-8')
+ req.add_header('Accept','application/xml; charset=UTF-8')
+ return req
+
+def _parse_google_checkout_response(response_xml):
+ redirect_url = ''
+ xml_doc = minidom.parseString(response_xml)
+ root = xml_doc.documentElement
+ node = root.childNodes[1]
+ if node.tagName == 'redirect-url':
+ redirect_url = node.firstChild.data
+ if node.tagName == 'error-message':
+ raise RuntimeError(node.firstChild.data)
+ return redirect_url
+
+def _build_xml_shopping_cart(request):
+ doc = Document()
+ root = doc.createElement('checkout-shopping-cart')
+ root.setAttribute('xmlns', 'http://checkout.google.com/schema/2')
+ doc.appendChild(root)
+ shopping_cart = doc.createElement('shopping-cart')
+ root.appendChild(shopping_cart)
+ items = doc.createElement('items')
+ shopping_cart.appendChild(items)
+ cart_items = cart.get_cart_items(request)
+
+ for cart_item in cart_items:
+ item = doc.createElement('item')
+ items.appendChild(item)
+ item_name = doc.createElement('item-name')
+ item_name_text = doc.createTextNode(str(cart_item.name))
+ item_name.appendChild(item_name_text)
+ item.appendChild(item_name)
+ item_description = doc.createElement('item-description')
+ item_description_text = doc.createTextNode(str(cart_item.name))
+ item_description.appendChild(item_description_text)
+ item.appendChild(item_description)
+ unit_price = doc.createElement('unit-price')
+ unit_price.setAttribute('currency', 'USD')
+ unit_price_text = doc.createTextNode(str(cart_item.price))
+ unit_price.appendChild(unit_price_text)
+ item.appendChild(unit_price)
+ quantity = doc.createElement('quantity')
+ quantity_text = doc.createTextNode(str(cart_item.quantity))
+ quantity.appendChild(quantity_text)
+ item.appendChild(quantity)
+
+ checkout_flow = doc.createElement('checkout-flow-support')
+ root.appendChild(checkout_flow)
+ merchant_flow = doc.createElement('merchant-checkout-flow-support')
+ checkout_flow.appendChild(merchant_flow)
+ shipping_methods = doc.createElement('shipping-methods')
+ merchant_flow.appendChild(shipping_methods)
+ flat_rate_shipping = doc.createElement('flat-rate-shipping')
+ flat_rate_shipping.setAttribute('name', 'FedEx Ground')
+ shipping_methods.appendChild(flat_rate_shipping)
+ shipping_price = doc.createElement('price')
+ shipping_price.setAttribute('currency', 'USD')
+ flat_rate_shipping.appendChild(shipping_price)
+ shipping_price_text = doc.createTextNode('9.99')
+ shipping_price.appendChild(shipping_price_text)
+ return doc.toxml(encoding='utf-8')
View
78 checkout/models.py
@@ -0,0 +1,78 @@
+from django.db import models
+from django import forms
+from django.contrib.auth.models import User
+from ecomstore.catalog.models import Product
+import decimal
+
+class Order(models.Model):
+ # each individual status
+ SUBMITTED = 1
+ PROCESSED = 2
+ SHIPPED = 3
+ CANCELLED = 4
+ # set of possible order statuses
+ ORDER_STATUSES = ((SUBMITTED, 'Submitted'),
+ (PROCESSED, 'Processed'),
+ (SHIPPED, 'Shipped'),
+ (CANCELLED, 'Cancelled'),)
+ # order info
+ date = models.DateTimeField(auto_now_add=True)
+ status = models.IntegerField(choices=ORDER_STATUSES, default=SUBMITTED)
+ ip_address = models.IPAddressField()
+ last_updated = models.DateTimeField(auto_now=True)
+ user = models.ForeignKey(User, null=True)
+ transaction_id = models.CharField(max_length=20)
+ # contact info
+ email = models.EmailField(max_length=50)
+ phone = models.CharField(max_length=20)
+ # shipping information
+ shipping_name = models.CharField(max_length=50)
+ shipping_address_1 = models.CharField(max_length=50)
+ shipping_address_2 = models.CharField(max_length=50, blank=True)
+ shipping_city = models.CharField(max_length=50)
+ shipping_state = models.CharField(max_length=2)
+ shipping_country = models.CharField(max_length=50)
+ shipping_zip = models.CharField(max_length=10)
+ # billing information
+ billing_name = models.CharField(max_length=50)
+ billing_address_1 = models.CharField(max_length=50)
+ billing_address_2 = models.CharField(max_length=50, blank=True)
+ billing_city = models.CharField(max_length=50)
+ billing_state = models.CharField(max_length=2)
+ billing_country = models.CharField(max_length=50)
+ billing_zip = models.CharField(max_length=10)
+
+ def __unicode__(self):
+ return 'Order #' + str(self.id)
+
+ @property
+ def total(self):
+ total = decimal.Decimal('0.00')
+ order_items = OrderItem.objects.filter(order=self)
+ for item in order_items:
+ total += item.total
+ return total
+
+class OrderItem(models.Model):
+ product = models.ForeignKey(Product)
+ quantity = models.IntegerField(default=1)
+ price = models.DecimalField(max_digits=9, decimal_places=2)
+ order = models.ForeignKey(Order)
+
+ @property
+ def total(self):
+ return self.quantity * self.price
+
+ @property
+ def name(self):
+ return self.product.name
+
+ @property
+ def sku(self):
+ return self.product.sku
+
+ def __unicode__(self):
+ return self.product.name + ' (' + self.product.sku + ')'
+
+ def get_absolute_url(self):
+ return self.product.get_absolute_url()
View
0 checkout/templatetags/__init__.py
No changes.
View
7 checkout/templatetags/checkout_tags.py
@@ -0,0 +1,7 @@
+from django import template
+
+register = template.Library()
+
+@register.inclusion_tag("tags/form_table_row.html")
+def form_table_row(form_field):
+ return {'form_field': form_field }
View
23 checkout/tests.py
@@ -0,0 +1,23 @@
+"""
+This file demonstrates two different styles of tests (one doctest and one
+unittest). These will both pass when you run "manage.py test".
+
+Replace these with more appropriate tests for your application.
+"""
+
+from django.test import TestCase
+
+class SimpleTest(TestCase):
+ def test_basic_addition(self):
+ """
+ Tests that 1 + 1 always equals 2.
+ """
+ self.failUnlessEqual(1 + 1, 2)
+
+__test__ = {"doctest": """
+Another way to test that 1 + 1 is equal to 2.
+
+>>> 1 + 1 == 2
+True
+"""}
+
View
11 checkout/urls.py
@@ -0,0 +1,11 @@
+from django.conf.urls.defaults import *
+from ecomstore import settings
+
+urlpatterns = patterns('ecomstore.checkout.views',
+ (r'^$', 'show_checkout', {'template_name': 'checkout/checkout.html',
+ 'SSL': settings.ENABLE_SSL
+ }, 'checkout'),
+ (r'^receipt/$', 'receipt', {'template_name': 'checkout/receipt.html',
+ 'SSL': settings.ENABLE_SSL
+ },'checkout_receipt'),
+)
View
44 checkout/views.py
@@ -0,0 +1,44 @@
+from django.shortcuts import render_to_response
+from django.template import RequestContext
+from django.core import urlresolvers
+from django.http import HttpResponseRedirect
+
+from ecomstore.checkout.forms import CheckoutForm
+from ecomstore.checkout.models import Order, OrderItem
+from ecomstore.checkout import checkout
+from ecomstore.cart import cart
+
+def show_checkout(request, template_name='checkout/checkout.html'):
+ if cart.is_empty(request):
+ cart_url = urlresolvers.reverse('show_cart')
+ return HttpResponseRedirect(cart_url)
+ if request.method == 'POST':
+ postdata = request.POST.copy()
+ form = CheckoutForm(postdata)
+ if form.is_valid():
+ response = checkout.process(request)
+ order_number = response.get('order_number', 0)
+ error_message = response.get('message', '')
+ if order_number:
+ request.session['order_number'] = order_number
+ receipt_url = urlresolvers.reverse('checkout_receipt')
+ return HttpResponseRedirect(receipt_url)
+ else:
+ error_message = 'Correct the errors below'
+ else:
+ form = CheckoutForm()
+ page_title = 'Checkout'
+ return render_to_response(template_name, locals(),
+ context_instance=RequestContext(request))
+
+def receipt(request, template_name='checkout/receipt.html'):
+ order_number = request.session.get('order_number', '')
+ if order_number:
+ order = Order.objects.filter(id=order_number)[0]
+ order_items = OrderItem.objects.filter(order=order)
+ del request.session['order_number']
+ else:
+ cart_url = urlresolvers.reverse('show_cart')
+ return HttpResponseRedirect(cart_url)
+ return render_to_response(template_name, locals(),
+ context_instance=RequestContext(request))
View
12 settings.py
@@ -3,6 +3,8 @@
DEBUG = True
TEMPLATE_DEBUG = DEBUG
+ENABLE_SSL = not DEBUG
+
ADMINS = (
# ('Your Name', 'your_email@domain.com'),
)
@@ -62,6 +64,7 @@
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware',
+ 'ecomstore.SSLMiddleware.SSLRedirect',
'djangodblog.middleware.DBLogMiddleware',
)
@@ -86,6 +89,7 @@
'ecomstore.utils',
'ecomstore.cart',
'django.contrib.flatpages',
+ 'ecomstore.checkout',
'djangodblog',
)
@@ -103,4 +107,12 @@
'ecomstore.utils.context_processors.ecomstore',
)
+GOOGLE_CHECKOUT_MERCHANT_ID = 'your id here'
+GOOGLE_CHECKOUT_MERCHANT_KEY = 'your key here'
+GOOGLE_CHECKOUT_URL = 'https://sandbox.google.com/checkout/ \
+ api/v2/merchantCheckout/Merchant/' + GOOGLE_CHECKOUT_MERCHANT_ID
+AUTHNET_POST_URL = 'test.authorize.net'
+AUTHNET_POST_PATH = '/gateway/transact.dll'
+AUTHNET_LOGIN = 'your login hear'
+AUTHNET_KEY = 'your key here'
View
27 static/css.css
@@ -169,4 +169,29 @@ div#footer a{
font-weight:normal;
margin-right:10px;
}
-
+fieldset.checkout{
+ padding-bottom:15px;
+ margin-bottom:15px;
+}
+fieldset.checkout legend{
+ font-weight:bold;
+ color:Black;
+ margin-left:15px;
+}
+fieldset.checkout table{
+ border-collapse:collapse;
+ width:100%;
+}
+fieldset.checkout table th{
+ text-align:right;
+ width:200px;
+ font-weight:normal;
+}
+fieldset.checkout input {
+ font-family:Courier;
+ font-size:Large;
+}
+table#receipt{
+ width:100%;
+ border-collapse:collapse;
+}
View
9 templates/cart/cart.html
@@ -25,7 +25,14 @@
{% if cart_items %}
<tr>
<th class="right" colspan="6">
- <a href="/url/to/checkout/">Checkout Now</a>
+ <form method="post" action=".">
+ <input type="hidden" name="submit" value="Checkout" />
+ <input type="image" name="Google Checkout" alt="Fast checkout through Google"
+ src="http://sandbox.google.com/checkout/buttons/checkout.gif?merchant_id={{ merchant_id }}&w=180&h=46&style=white&variant=text&loc=en_US" height="46" width="180" />
+ <input type="submit" name="submit" value="Checkout" alt="Checkout" />
+ </form>
+
+
</th>
</tr>
{% endif %}
View
57 templates/checkout/checkout.html
@@ -0,0 +1,57 @@
+{% extends "catalog.html" %}
+{% load checkout_tags %}
+{% block content %}
+<h1>Checkout</h1>{% if error_message %}
+ <ul class="errorlist">
+ <li>{{ error_message }}</li>
+ </ul>
+{% endif %}
+<form action="." method="post">
+ <fieldset class="checkout">
+ <legend>Contact Info</legend>
+ <table>
+ {% form_table_row form.email %}
+ {% form_table_row form.phone %}
+ </table>
+ </fieldset>
+ <fieldset class="checkout">
+ <legend>Shipping Info</legend>
+ <table>
+ {% form_table_row form.shipping_name %}
+ {% form_table_row form.shipping_address_1 %}
+ {% form_table_row form.shipping_address_2 %}
+ {% form_table_row form.shipping_city %}
+ {% form_table_row form.shipping_state %}
+ {% form_table_row form.shipping_zip %}
+ {% form_table_row form.shipping_country %}
+ </table>
+ </fieldset>
+ <fieldset class="checkout">
+ <legend>Billing Info</legend>
+ <table>
+ {% form_table_row form.billing_name %}
+ {% form_table_row form.billing_address_1 %}
+ {% form_table_row form.billing_address_2 %}
+ {% form_table_row form.billing_city %}
+ {% form_table_row form.billing_state %}
+ {% form_table_row form.billing_zip %}
+ {% form_table_row form.billing_country %}
+ </table>
+ </fieldset>
+ <fieldset class="checkout">
+ <legend>Credit Card Info</legend>
+ <table>
+ {% form_table_row form.credit_card_number %}
+ {% form_table_row form.credit_card_type %}
+ {% form_table_row form.credit_card_expire_month %}
+ {% form_table_row form.credit_card_expire_year %}
+ {% form_table_row form.credit_card_cvv %}
+ </table>
+ </fieldset>
+ <table>
+ <tr>
+ <th colspan="2"><input type="submit" value="Place Order" class="submit" /></th>
+ </tr>
+ </table>
+</form>
+{% endblock %}
View
34 templates/checkout/receipt.html
@@ -0,0 +1,34 @@
+{% extends "catalog.html" %}
+{% load catalog_filters %}
+{% block content %}
+ <table id="receipt">
+ <caption>Your order has been placed!<br /><br />
+ Your Order Number is: {{ order.id }}
+ </caption>
+ <thead>
+ <tr>
+ <th scope="col">Name</th>
+ <th scope="col">Price</th>
+ <th scope="col">Quantity</th>
+ <th class="right" scope="col">Total</th>
+ </tr>
+ </thead>
+ <tfoot>
+ <tr>
+ <td colspan="4" class="right" style="height:30px;">
+ Order Total: {{ order.total|currency }}
+ </td>
+ </tr>
+ </tfoot>
+ <tbody>
+ {% for item in order_items %}
+ <tr>
+ <td>{{ item.name }}</td>
+ <td>{{ item.price|currency }}</td>
+ <td>{{ item.quantity }}</td>
+ <td class="right">{{ item.total|currency }}</td>
+ </tr>
+ {% endfor %}
+ </tbody>
+ </table>
+{% endblock %}
View
16 templates/tags/form_table_row.html
@@ -0,0 +1,16 @@
+<tr>
+ <th>
+ {% if form_field.field.required %}*{% endif %}
+ {{ form_field.label_tag }}
+ </th>
+ <td>
+ {% if form_field.errors %}
+ <ul class="errorlist">
+ {% for error in form_field.errors %}
+ <li>{{ form_field.label }}: {{ error }}</li>
+ {% endfor %}
+ </ul>
+ {% endif %}
+ {{ form_field }}
+ </td>
+</tr>
View
1 urls.py
@@ -14,6 +14,7 @@
(r'^admin/', include(admin.site.urls)),
(r'^', include('catalog.urls')),
(r'^cart/', include('cart.urls')),
+ (r'^checkout/', include('checkout.urls')),
)
if settings.DEBUG:

0 comments on commit 719d8b8

Please sign in to comment.
Something went wrong with that request. Please try again.