├── LICENSE ├── MANIFEST.in ├── Makefile ├── README.rst ├── oscar_stripe ├── __init__.py └── models.py ├── release.sh ├── requirements.txt ├── runtests.py ├── sandbox ├── apps │ ├── __init__.py │ ├── app.py │ └── checkout │ │ ├── __init__.py │ │ ├── app.py │ │ ├── models.py │ │ └── views.py ├── manage.py ├── settings.py ├── templates │ └── checkout │ │ ├── payment_details.html │ │ ├── preview.html │ │ └── thank_you.html └── urls.py ├── setup.py └── tests └── tests.py /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011, Tangent Communications PLC and individual contributors. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of Tangent Communications PLC nor the names of its contributors 15 | may be used to endorse or promote products derived from this software without 16 | specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include *.rst 2 | recursive-include oscar_stripe/templates *.html 3 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | install: 2 | python setup.py develop 3 | pip install -r requirements.txt 4 | 5 | sandbox: install 6 | python sandbox/manage.py syncdb --noinput 7 | python sandbox/manage.py migrate 8 | 9 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | =================================== 2 | Stripe integration for django-oscar 3 | =================================== 4 | 5 | *This repo is just a skeleton at the moment. It isn't complete yet.* 6 | 7 | This package provides integration between Stripe_ and Oscar_. It is currently a 8 | work-in-progress - contributions are welcome. Any questions, please use the Oscar mailing list: `django-oscar@googlegroups.com`_ 9 | 10 | .. _Stripe: https://stripe.com 11 | .. _Oscar: http://oscarcommerce.com 12 | .. _`django-oscar@googlegroups.com`: https://groups.google.com/forum/?fromgroups#!forum/django-oscar 13 | 14 | Useful information: 15 | 16 | * `Stripe's Python API`_ 17 | 18 | .. _`Stripe's Python API`: https://stripe.com/docs/libraries 19 | 20 | Contributing 21 | ============ 22 | 23 | Clone the repo, create a virtualenv and run:: 24 | 25 | make install 26 | 27 | to install all dependencies. Run the tests with:: 28 | 29 | ./runtests.py 30 | 31 | There is a sandbox site that you can browse and use to test the Stripe 32 | integration. Create is using:: 33 | 34 | make sandbox 35 | 36 | and browse it after:: 37 | 38 | cd sandbox 39 | ./manage.py runserver 40 | 41 | TODO 42 | ==== 43 | 44 | * In the sandbox, override the PaymentDetailsView to charge a user's card before 45 | placing the order. 46 | * Create a "Stripe transaction" model that tracks each request/response to 47 | Stripe's servers 48 | * Investigate if we need a facade module like in the other Oscar extension libs. 49 | Their API is so simple, I'm not sure we do. 50 | -------------------------------------------------------------------------------- /oscar_stripe/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/django-oscar/django-oscar-stripe/2845b8c7c0133f72d853cc9d0708558df9a7414e/oscar_stripe/__init__.py -------------------------------------------------------------------------------- /oscar_stripe/models.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/django-oscar/django-oscar-stripe/2845b8c7c0133f72d853cc9d0708558df9a7414e/oscar_stripe/models.py -------------------------------------------------------------------------------- /release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Should always publish from master 4 | git checkout master 5 | 6 | # Push to PyPi 7 | ./setup.py sdist upload 8 | 9 | # Push commits and tag to remote 10 | git push --tags 11 | git push -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | South>=0.7.3,<0.8 2 | 3 | # Developing 4 | Werkzeug 5 | django-debug-toolbar 6 | django-extensions 7 | 8 | # Testing 9 | mock>=1.0.1,<1.1 10 | django-nose>=1.1,<1.2 11 | pinocchio>=0.3.1,<=0.4 12 | coverage>=3.6,<3.7 13 | -------------------------------------------------------------------------------- /runtests.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import sys 3 | from coverage import coverage 4 | from optparse import OptionParser 5 | 6 | from django.conf import settings 7 | 8 | 9 | if not settings.configured: 10 | settings.configure( 11 | DATABASES={ 12 | 'default': { 13 | 'ENGINE': 'django.db.backends.sqlite3', 14 | } 15 | }, 16 | INSTALLED_APPS=[ 17 | 'django.contrib.auth', 18 | 'django.contrib.admin', 19 | 'django.contrib.contenttypes', 20 | 'django.contrib.sessions', 21 | 'django.contrib.sites', 22 | 'oscar_stripe', 23 | 'south', 24 | ], 25 | DEBUG=False, 26 | SITE_ID=1, 27 | NOSE_ARGS=['-s', '--with-spec'], 28 | ) 29 | 30 | # Needs to be here to avoid missing SETTINGS env var 31 | from django_nose import NoseTestSuiteRunner 32 | 33 | 34 | def run_tests(*test_args): 35 | if 'south' in settings.INSTALLED_APPS: 36 | from south.management.commands import patch_for_test_db_setup 37 | patch_for_test_db_setup() 38 | 39 | if not test_args: 40 | test_args = ['tests'] 41 | 42 | # Run tests 43 | test_runner = NoseTestSuiteRunner(verbosity=1) 44 | 45 | c = coverage(source=['oscar_stripe'], omit=['*migrations*', '*tests*']) 46 | c.start() 47 | num_failures = test_runner.run_tests(test_args) 48 | c.stop() 49 | 50 | if num_failures > 0: 51 | sys.exit(num_failures) 52 | print "Generating HTML coverage report" 53 | c.html_report() 54 | 55 | 56 | if __name__ == '__main__': 57 | parser = OptionParser() 58 | (options, args) = parser.parse_args() 59 | run_tests(*args) 60 | -------------------------------------------------------------------------------- /sandbox/apps/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/django-oscar/django-oscar-stripe/2845b8c7c0133f72d853cc9d0708558df9a7414e/sandbox/apps/__init__.py -------------------------------------------------------------------------------- /sandbox/apps/app.py: -------------------------------------------------------------------------------- 1 | from oscar.app import Shop 2 | from apps.checkout.app import application as checkout_app 3 | 4 | 5 | class StripeShop(Shop): 6 | # Specify a local checkout app where we override the payment details view 7 | checkout_app = checkout_app 8 | 9 | 10 | shop = StripeShop() 11 | -------------------------------------------------------------------------------- /sandbox/apps/checkout/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/django-oscar/django-oscar-stripe/2845b8c7c0133f72d853cc9d0708558df9a7414e/sandbox/apps/checkout/__init__.py -------------------------------------------------------------------------------- /sandbox/apps/checkout/app.py: -------------------------------------------------------------------------------- 1 | from oscar.apps.checkout.app import CheckoutApplication 2 | 3 | from apps.checkout import views 4 | 5 | 6 | class OverriddenCheckoutApplication(CheckoutApplication): 7 | # Specify new view for payment details 8 | payment_details_view = views.PaymentDetailsView 9 | 10 | 11 | application = OverriddenCheckoutApplication() 12 | -------------------------------------------------------------------------------- /sandbox/apps/checkout/models.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/django-oscar/django-oscar-stripe/2845b8c7c0133f72d853cc9d0708558df9a7414e/sandbox/apps/checkout/models.py -------------------------------------------------------------------------------- /sandbox/apps/checkout/views.py: -------------------------------------------------------------------------------- 1 | from oscar.apps.checkout.views import PaymentDetailsView as OscarPaymentDetailsView 2 | 3 | 4 | class PaymentDetailsView(OscarPaymentDetailsView): 5 | pass 6 | 7 | -------------------------------------------------------------------------------- /sandbox/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings") 7 | 8 | from django.core.management import execute_from_command_line 9 | 10 | execute_from_command_line(sys.argv) 11 | -------------------------------------------------------------------------------- /sandbox/settings.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | # Django settings for oscar project. 4 | PROJECT_DIR = os.path.dirname(__file__) 5 | location = lambda x: os.path.join(os.path.dirname(os.path.realpath(__file__)), x) 6 | 7 | DEBUG = True 8 | TEMPLATE_DEBUG = True 9 | SQL_DEBUG = True 10 | 11 | ADMINS = ( 12 | # ('Your Name', 'your_email@domain.com'), 13 | ) 14 | EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' 15 | 16 | MANAGERS = ADMINS 17 | 18 | DATABASES = { 19 | 'default': { 20 | 'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'. 21 | 'NAME': location('db.sqlite'), # Or path to database file if using sqlite3. 22 | 'USER': '', # Not used with sqlite3. 23 | 'PASSWORD': '', # Not used with sqlite3. 24 | 'HOST': '', # Set to empty string for localhost. Not used with sqlite3. 25 | 'PORT': '', # Set to empty string for default. Not used with sqlite3. 26 | } 27 | } 28 | 29 | # Local time zone for this installation. Choices can be found here: 30 | # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name 31 | # although not all choices may be available on all operating systems. 32 | # On Unix systems, a value of None will cause Django to use the same 33 | # timezone as the operating system. 34 | # If running in a Windows environment this must be set to the same as your 35 | # system time zone. 36 | TIME_ZONE = 'Europe/London' 37 | 38 | # Language code for this installation. All choices can be found here: 39 | # http://www.i18nguy.com/unicode/language-identifiers.html 40 | LANGUAGE_CODE = 'en-us' 41 | 42 | SITE_ID = 1 43 | 44 | # If you set this to False, Django will make some optimizations so as not 45 | # to load the internationalization machinery. 46 | USE_I18N = True 47 | 48 | # If you set this to False, Django will not format dates, numbers and 49 | # calendars according to the current locale 50 | USE_L10N = True 51 | 52 | # Absolute path to the directory that holds media. 53 | # Example: "/home/media/media.lawrence.com/" 54 | MEDIA_ROOT = location("public/media") 55 | 56 | # URL that handles the media served from MEDIA_ROOT. Make sure to use a 57 | # trailing slash if there is a path component (optional in other cases). 58 | # Examples: "http://media.lawrence.com", "http://example.com/media/" 59 | MEDIA_URL = '/media/' 60 | 61 | # URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a 62 | # trailing slash. 63 | # Examples: "http://foo.com/media/", "/media/". 64 | #ADMIN_MEDIA_PREFIX = '/media/admin/' 65 | 66 | STATIC_URL = '/static/' 67 | STATICFILES_DIRS = (location('static/'),) 68 | STATIC_ROOT = location('public/static') 69 | 70 | # Make this unique, and don't share it with anybody. 71 | SECRET_KEY = '$)a7n&o80u!6y5t-+jrd3)3!%vh&shg$wqpjpxc!ar&p#!)n1a' 72 | 73 | # List of callables that know how to import templates from various sources. 74 | TEMPLATE_LOADERS = ( 75 | 'django.template.loaders.filesystem.Loader', 76 | 'django.template.loaders.app_directories.Loader', 77 | # 'django.template.loaders.eggs.Loader', 78 | ) 79 | 80 | TEMPLATE_CONTEXT_PROCESSORS = ( 81 | "django.contrib.auth.context_processors.auth", 82 | "django.core.context_processors.request", 83 | "django.core.context_processors.debug", 84 | "django.core.context_processors.i18n", 85 | "django.core.context_processors.media", 86 | "django.core.context_processors.static", 87 | "django.contrib.messages.context_processors.messages", 88 | # Oscar specific 89 | 'oscar.apps.search.context_processors.search_form', 90 | 'oscar.apps.promotions.context_processors.promotions', 91 | 'oscar.apps.checkout.context_processors.checkout', 92 | 'oscar.core.context_processors.metadata', 93 | ) 94 | 95 | MIDDLEWARE_CLASSES = ( 96 | 'django.middleware.common.CommonMiddleware', 97 | 'django.contrib.sessions.middleware.SessionMiddleware', 98 | 'django.middleware.csrf.CsrfViewMiddleware', 99 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 100 | 'django.contrib.messages.middleware.MessageMiddleware', 101 | 'django.middleware.transaction.TransactionMiddleware', 102 | 'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware', 103 | 'debug_toolbar.middleware.DebugToolbarMiddleware', 104 | 'oscar.apps.basket.middleware.BasketMiddleware', 105 | ) 106 | 107 | INTERNAL_IPS = ('127.0.0.1',) 108 | 109 | ROOT_URLCONF = 'urls' 110 | 111 | from oscar import OSCAR_MAIN_TEMPLATE_DIR 112 | TEMPLATE_DIRS = ( 113 | location('templates'), 114 | OSCAR_MAIN_TEMPLATE_DIR, 115 | ) 116 | 117 | # A sample logging configuration. The only tangible logging 118 | # performed by this configuration is to send an email to 119 | # the site admins on every HTTP 500 error. 120 | # See http://docs.djangoproject.com/en/dev/topics/logging for 121 | # more details on how to customize your logging configuration. 122 | LOGGING = { 123 | 'version': 1, 124 | 'disable_existing_loggers': False, 125 | 'formatters': { 126 | 'verbose': { 127 | 'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s' 128 | }, 129 | 'simple': { 130 | 'format': '%(levelname)s %(message)s' 131 | }, 132 | }, 133 | 'handlers': { 134 | 'null': { 135 | 'level':'DEBUG', 136 | 'class':'django.utils.log.NullHandler', 137 | }, 138 | 'console':{ 139 | 'level':'DEBUG', 140 | 'class':'logging.StreamHandler', 141 | 'formatter': 'verbose' 142 | }, 143 | 'mail_admins': { 144 | 'level': 'ERROR', 145 | 'class': 'django.utils.log.AdminEmailHandler', 146 | }, 147 | }, 148 | 'loggers': { 149 | 'django': { 150 | 'handlers':['null'], 151 | 'propagate': True, 152 | 'level':'INFO', 153 | }, 154 | 'django.request': { 155 | 'handlers': ['mail_admins'], 156 | 'level': 'ERROR', 157 | 'propagate': False, 158 | }, 159 | 'oscar.checkout': { 160 | 'handlers': ['console'], 161 | 'propagate': True, 162 | 'level':'INFO', 163 | }, 164 | 'django.db.backends': { 165 | 'handlers':['null'], 166 | 'propagate': False, 167 | 'level':'DEBUG', 168 | }, 169 | } 170 | } 171 | 172 | 173 | INSTALLED_APPS = [ 174 | 'django.contrib.auth', 175 | 'django.contrib.contenttypes', 176 | 'django.contrib.sessions', 177 | 'django.contrib.sites', 178 | 'django.contrib.messages', 179 | 'django.contrib.admin', 180 | 'django.contrib.flatpages', 181 | 'django.contrib.staticfiles', 182 | # External apps 183 | 'django_extensions', 184 | 'debug_toolbar', 185 | 'oscar_stripe', 186 | 'south', 187 | ] 188 | from oscar import get_core_apps 189 | INSTALLED_APPS += get_core_apps() 190 | 191 | AUTHENTICATION_BACKENDS = ( 192 | 'oscar.apps.customer.auth_backends.Emailbackend', 193 | 'django.contrib.auth.backends.ModelBackend', 194 | ) 195 | 196 | LOGIN_REDIRECT_URL = '/accounts/' 197 | APPEND_SLASH = True 198 | 199 | DEBUG_TOOLBAR_CONFIG = { 200 | 'INTERCEPT_REDIRECTS': False 201 | } 202 | 203 | # Oscar settings 204 | from oscar.defaults import * 205 | OSCAR_ALLOW_ANON_CHECKOUT = True 206 | 207 | # Haystack settings 208 | HAYSTACK_CONNECTIONS = { 209 | 'default': { 210 | 'ENGINE': 'haystack.backends.simple_backend.SimpleEngine', 211 | }, 212 | } 213 | 214 | OSCAR_SHOP_TAGLINE = 'Stripe sandbox' 215 | -------------------------------------------------------------------------------- /sandbox/templates/checkout/payment_details.html: -------------------------------------------------------------------------------- 1 | {% extends 'templates/checkout/payment_details.html' %} 2 | 3 | {% block payment_details_content %} 4 | 5 |
12 | 13 | {% endblock %} -------------------------------------------------------------------------------- /sandbox/templates/checkout/preview.html: -------------------------------------------------------------------------------- 1 | {% extends 'templates/checkout/preview.html' %} 2 | {% load currency_filters %} 3 | 4 | {% block payment_method %} 5 |{{ order_total_incl_tax|currency }} will be debited from your bankcard.
11 |