From eefe89849001b837631ef2fe66e89975687312bc Mon Sep 17 00:00:00 2001 From: Guillaume Pellerin Date: Thu, 24 Sep 2015 04:06:22 +0200 Subject: [PATCH] get first PayPal WPS payment method (not fully tested yet) --- .gitignore | 2 +- Dockerfile | 10 +- diggersdigest/deploy/start_app.sh | 27 +++ diggersdigest/diggersdigest/local_settings.py | 66 ------ diggersdigest/diggersdigest/settings.py | 197 +++++++++++------- diggersdigest/diggersdigest/urls.py | 2 + diggersdigest/records/models.py | 64 +++++- diggersdigest/static/img/favicon.ico | Bin 892 -> 318 bytes .../shop/includes/payment_fields.html | 30 +-- diggersdigest/templates/shop/payment.html | 56 ++++- docker-compose.yml | 6 +- requirements-dev.txt | 3 + requirements.txt | 7 +- 13 files changed, 302 insertions(+), 168 deletions(-) create mode 100644 diggersdigest/deploy/start_app.sh create mode 100644 requirements-dev.txt diff --git a/.gitignore b/.gitignore index 775ff5e..5db0362 100644 --- a/.gitignore +++ b/.gitignore @@ -27,4 +27,4 @@ pip-log.txt #Volume Directory for docker-compose mysql/ -diggersdigest/static/media \ No newline at end of file +diggersdigest/static/media diff --git a/Dockerfile b/Dockerfile index e05c7ca..482e1de 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,17 +1,25 @@ FROM python:2.7 ENV PYTHONUNBUFFERED 1 + RUN apt-get update &&\ apt-get -y install locales && \ echo fr_FR.UTF-8 UTF-8 >> /etc/locale.gen &&\ locale-gen + ENV LANG fr_FR.UTF-8 ENV LANGUAGE fr_FR:fr ENV LC_ALL fr_FR.UTF-8 RUN mkdir /code +RUN mkdir /opt/src + WORKDIR /code + ADD requirements.txt /code/ RUN pip install -r requirements.txt -ADD . /code/ +ADD requirements-dev.txt /code/ +RUN pip install -r requirements-dev.txt --src /opt/src + +ADD . /code/ diff --git a/diggersdigest/deploy/start_app.sh b/diggersdigest/deploy/start_app.sh new file mode 100644 index 0000000..a58577f --- /dev/null +++ b/diggersdigest/deploy/start_app.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +app='diggersdigest' + +# paths +root_dir='/code' +app_dir=$root_dir'/'$app +static=$root_dir'/static/' +sandbox=$app_dir +manage=$sandbox'/manage.py' +wsgi=$sandbox'/wsgi.py' + +# waiting for other services +# sh $app_dir/examples/deploy/wait.sh + +# django init +python $manage migrate --noinput +python $manage collectstatic --noinput + +# static files auto update +#watchmedo shell-command --patterns="*.js;*.css" --recursive \ +# --command='python '$manage' collectstatic --noinput' $static & + +# app start +#uwsgi --socket :8000 --wsgi-file $wsgi --chdir $sandbox --master --processes 4 --threads 2 --py-autoreload 3 + +python $manage runserver 0.0.0.0:8000 diff --git a/diggersdigest/diggersdigest/local_settings.py b/diggersdigest/diggersdigest/local_settings.py index 2c50da4..188f580 100644 --- a/diggersdigest/diggersdigest/local_settings.py +++ b/diggersdigest/diggersdigest/local_settings.py @@ -16,19 +16,6 @@ DATABASES = { } } -####################### -# FILEBROWSER settings -####################### -# -# Max. Upload Size in Bytes: -# 10MB - 10485760 -# 20MB - 20971520 -# 50MB - 5242880 -# 100MB 104857600 -# 250MB - 214958080 -# 500MB - 429916160 -FILEBROWSER_MAX_UPLOAD_SIZE = 104857600 - # EXTENSIONS AND FORMATS # Allowed Extensions for File Upload. Lower case is important. FILEBROWSER_EXTENSIONS = { @@ -54,59 +41,6 @@ FILEBROWSER_SELECT_FORMATS = { 'audio': ['Audio'], } - -######### -# Shop -######### - -# Add Migration Module path see : https://github.com/stephenmcd/mezzanine/blob/master/docs/model-customization.rst#field-injection-caveats -MIGRATION_MODULES = { - "shop": "diggersdigest.migrations.shop", - "blog": "diggersdigest.migrations.blog" -} - -# USE or EXTEND the custom callback-uuid form -SHOP_CHECKOUT_FORM_CLASS = 'payments.multipayments.forms.base.CallbackUUIDOrderForm' - -PRIMARY_PAYMENT_PROCESSOR_IN_USE = True - -SECONDARY_PAYMENT_PROCESSORS = ( -# ... -('paypal', { -'name' : 'Pay With Pay-Pal', -'form' : 'payments.multipayments.forms.paypal.PaypalSubmissionForm' -}), -# ... -) - -# Currency type. -PAYPAL_CURRENCY = "EUR" - -# Business account email. Sandbox emails look like this. -PAYPAL_BUSINESS = 'pellerin@parisson.com' -PAYPAL_RECEIVER_EMAIL = PAYPAL_BUSINESS - -# Use this to enable https on return URLs. This is strongly recommended! (Except for sandbox) -PAYPAL_RETURN_WITH_HTTPS = False - -# Function that returns args for `reverse`. -# URL is sent to PayPal as the for returning to a 'complete' landing page. -PAYPAL_RETURN_URL = lambda cart, uuid, order_form: ('shop_complete', None, None) - -# Function that returns args for `reverse`. -# URL is sent to PayPal as the URL to callback to for PayPal IPN. -# Set to None if you do not wish to use IPN. -PAYPAL_IPN_URL = lambda cart, uuid, order_form: ('my_paypal_ipn', None, {'uuid' : uuid}) - -# URL the secondary-payment-form is submitted to -# Dev example -PAYPAL_SUBMIT_URL = 'https://www.sandbox.paypal.com/cgi-bin/webscr' -# Prod example -PAYPAL_SUBMIT_URL = 'https://www.paypal.com/cgi-bin/webscr' - -# For real use set to False -PAYPAL_TEST = True - ################### # DEPLOY SETTINGS # ################### diff --git a/diggersdigest/diggersdigest/settings.py b/diggersdigest/diggersdigest/settings.py index 0b2a82d..7bbd021 100644 --- a/diggersdigest/diggersdigest/settings.py +++ b/diggersdigest/diggersdigest/settings.py @@ -4,78 +4,6 @@ import os from django.utils.translation import ugettext_lazy as _ -###################### -# CARTRIDGE SETTINGS # -###################### - -# The following settings are already defined in cartridge.shop.defaults -# with default values, but are common enough to be put here, commented -# out, for conveniently overriding. Please consult the settings -# documentation for a full list of settings Cartridge implements: -# http://cartridge.jupo.org/configuration.html#default-settings - -# Sequence of available credit card types for payment. -# SHOP_CARD_TYPES = ("Mastercard", "Visa", "Diners", "Amex") - -# Setting to turn on featured images for shop categories. Defaults to False. -# SHOP_CATEGORY_USE_FEATURED_IMAGE = True - -# Set an alternative OrderForm class for the checkout process. -# SHOP_CHECKOUT_FORM_CLASS = 'cartridge.shop.forms.OrderForm' - -# If True, the checkout process is split into separate -# billing/shipping and payment steps. -# SHOP_CHECKOUT_STEPS_SPLIT = True - -# If True, the checkout process has a final confirmation step before -# completion. -# SHOP_CHECKOUT_STEPS_CONFIRMATION = True - -# Controls the formatting of monetary values accord to the locale -# module in the python standard library. If an empty string is -# used, will fall back to the system's locale. -#SHOP_CURRENCY_LOCALE = '' - -# Dotted package path and name of the function that -# is called on submit of the billing/shipping checkout step. This -# is where shipping calculation can be performed and set using the -# function ``cartridge.shop.utils.set_shipping``. -# SHOP_HANDLER_BILLING_SHIPPING = \ -# "cartridge.shop.checkout.default_billship_handler" - -# Dotted package path and name of the function that -# is called once an order is successful and all of the order -# object's data has been created. This is where any custom order -# processing should be implemented. -# SHOP_HANDLER_ORDER = "cartridge.shop.checkout.default_order_handler" - -# Dotted package path and name of the function that -# is called on submit of the payment checkout step. This is where -# integration with a payment gateway should be implemented. -# SHOP_HANDLER_PAYMENT = "cartridge.shop.checkout.default_payment_handler" - -# Sequence of value/name pairs for order statuses. -# SHOP_ORDER_STATUS_CHOICES = ( -# (1, "Unprocessed"), -# (2, "Processed"), -# ) - -# Sequence of value/name pairs for types of product options, -# eg Size, Colour. NOTE: Increasing the number of these will -# require database migrations! -#SHOP_OPTION_TYPE_CHOICES = ( -# (1, "Cover condition"), -# (2, "Vinyl condition"), -# ) - -SHOP_USE_VARIATIONS = False - -# Sequence of indexes from the SHOP_OPTION_TYPE_CHOICES setting that -# control how the options should be ordered in the admin, -# SHOP_OPTION_ADMIN_ORDER = (1, 2) - -SHOP_USE_RATINGS = False - ###################### # MEZZANINE SETTINGS # ###################### @@ -148,7 +76,7 @@ EXTRA_MODEL_FIELDS = ( "django.db.models.CharField", (), {"blank" : False, "max_length" : 36}, - ) + ), # ... # # Example of adding a field to *all* of Mezzanine's content types: # ( @@ -169,10 +97,133 @@ USE_MODELTRANSLATION = False SEARCH_MODEL_CHOICES = ('shop.Product',) + +###################### +# CARTRIDGE SETTINGS # +###################### + +# The following settings are already defined in cartridge.shop.defaults +# with default values, but are common enough to be put here, commented +# out, for conveniently overriding. Please consult the settings +# documentation for a full list of settings Cartridge implements: +# http://cartridge.jupo.org/configuration.html#default-settings + +# Sequence of available credit card types for payment. +# SHOP_CARD_TYPES = ("Mastercard", "Visa", "Diners", "Amex") + +# Setting to turn on featured images for shop categories. Defaults to False. +# SHOP_CATEGORY_USE_FEATURED_IMAGE = True + +# Set an alternative OrderForm class for the checkout process. +# SHOP_CHECKOUT_FORM_CLASS = 'cartridge.shop.forms.OrderForm' + +# If True, the checkout process is split into separate +# billing/shipping and payment steps. +# SHOP_CHECKOUT_STEPS_SPLIT = True + +# If True, the checkout process has a final confirmation step before +# completion. +# SHOP_CHECKOUT_STEPS_CONFIRMATION = True + +# Controls the formatting of monetary values accord to the locale +# module in the python standard library. If an empty string is +# used, will fall back to the system's locale. +#SHOP_CURRENCY_LOCALE = '' + +# Dotted package path and name of the function that +# is called on submit of the billing/shipping checkout step. This +# is where shipping calculation can be performed and set using the +# function ``cartridge.shop.utils.set_shipping``. +# SHOP_HANDLER_BILLING_SHIPPING = \ +# "cartridge.shop.checkout.default_billship_handler" + +# Dotted package path and name of the function that +# is called once an order is successful and all of the order +# object's data has been created. This is where any custom order +# processing should be implemented. +# SHOP_HANDLER_ORDER = "cartridge.shop.checkout.default_order_handler" + +# Dotted package path and name of the function that +# is called on submit of the payment checkout step. This is where +# integration with a payment gateway should be implemented. +# SHOP_HANDLER_PAYMENT = "cartridge.shop.checkout.default_payment_handler" + +# Sequence of value/name pairs for order statuses. +# SHOP_ORDER_STATUS_CHOICES = ( +# (1, "Unprocessed"), +# (2, "Processed"), +# ) + +# Sequence of value/name pairs for types of product options, +# eg Size, Colour. NOTE: Increasing the number of these will +# require database migrations! +#SHOP_OPTION_TYPE_CHOICES = ( +# (1, "Cover condition"), +# (2, "Vinyl condition"), +# ) + +SHOP_USE_VARIATIONS = False + +# Sequence of indexes from the SHOP_OPTION_TYPE_CHOICES setting that +# control how the options should be ordered in the admin, +# SHOP_OPTION_ADMIN_ORDER = (1, 2) + +SHOP_USE_RATINGS = False + +# Add Migration Module path see : https://github.com/stephenmcd/mezzanine/blob/master/docs/model-customization.rst#field-injection-caveats +MIGRATION_MODULES = { + "shop": "diggersdigest.migrations.shop", + "blog": "diggersdigest.migrations.blog" +} + +# USE or EXTEND the custom callback-uuid form +SHOP_CHECKOUT_FORM_CLASS = 'payments.multipayments.forms.base.CallbackUUIDOrderForm' + +PRIMARY_PAYMENT_PROCESSOR_IN_USE = False + +SECONDARY_PAYMENT_PROCESSORS = ( + ('paypal', { + 'name' : 'Pay With Pay-Pal', + 'form' : 'payments.multipayments.forms.paypal.PaypalSubmissionForm' + }), +) + + +# Currency type. +PAYPAL_CURRENCY = "EUR" + +# Business account email. Sandbox emails look like this. +PAYPAL_BUSINESS = 'pellerin@parisson.com' +PAYPAL_RECEIVER_EMAIL = PAYPAL_BUSINESS + +# Use this to enable https on return URLs. This is strongly recommended! (Except for sandbox) +PAYPAL_RETURN_WITH_HTTPS = False + +# Function that returns args for `reverse`. +# URL is sent to PayPal as the for returning to a 'complete' landing page. +PAYPAL_RETURN_URL = lambda cart, uuid, order_form: ('shop_complete', None, None) + +# Function that returns args for `reverse`. +# URL is sent to PayPal as the URL to callback to for PayPal IPN. +# Set to None if you do not wish to use IPN. +PAYPAL_IPN_URL = lambda cart, uuid, order_form: ('paypal.standard.ipn.views.ipn', None, {}) + + +# URL the secondary-payment-form is submitted to +# Dev example +PAYPAL_SUBMIT_URL = 'https://www.sandbox.paypal.com/cgi-bin/webscr' +# Prod example +# PAYPAL_SUBMIT_URL = 'https://www.paypal.com/cgi-bin/webscr' + +# For real use set to False +PAYPAL_TEST = True + ######################## # MAIN DJANGO SETTINGS # ######################## +SITE_DOMAIN = 'localhost' + # Hosts/domain names that are valid for this site; required if DEBUG is False # See https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts ALLOWED_HOSTS = [] diff --git a/diggersdigest/diggersdigest/urls.py b/diggersdigest/diggersdigest/urls.py index 7d5ee68..6efe25e 100644 --- a/diggersdigest/diggersdigest/urls.py +++ b/diggersdigest/diggersdigest/urls.py @@ -101,6 +101,8 @@ urlpatterns += patterns('', # ("^%s/" % settings.SITE_PREFIX, include("mezzanine.urls")) + (r'^paypal-ipn-8c5erc9ye49ia51rn655mi4xs7/', include('paypal.standard.ipn.urls')), + ) # Adds ``STATIC_URL`` to the context of error pages, so that error diff --git a/diggersdigest/records/models.py b/diggersdigest/records/models.py index 5fc1c51..277b13c 100644 --- a/diggersdigest/records/models.py +++ b/diggersdigest/records/models.py @@ -9,14 +9,19 @@ from django.utils.translation import ugettext_lazy as _ import datetime import os import fnmatch +from importlib import import_module from diggersdigest import settings +# from mezzanine.conf import settings from mezzanine.core.fields import FileField from mezzanine.core.models import CONTENT_STATUS_DRAFT, CONTENT_STATUS_PUBLISHED from mezzanine.blog.models import BlogPost from mezzanine.utils.models import upload_to -from cartridge.shop.models import Product, Category +from cartridge.shop.models import Product, Category, Cart, Order, ProductVariation, DiscountCode +from paypal.standard.ipn.signals import payment_was_successful +from paypal.standard.models import ST_PP_COMPLETED +from paypal.standard.ipn.signals import valid_ipn_received # Auto-generated Django models with manage.py inspectdb on the old database @@ -282,3 +287,60 @@ class Podcast(BlogPost): #visu1 = models.IntegerField() # ordre : on laisse tombé ? # published --> status / 0 --> CONTENT_STATUS_DRAFT = 1 / 1 CONTENT_STATUS_PUBLISHED = 2 + + +def payment_complete(sender, **kwargs): + """Performs the same logic as the code in + cartridge.shop.models.Order.complete(), but fetches the session, + order, and cart objects from storage, rather than relying on the + request object being passed in (which it isn't, since this is + triggered on PayPal IPN callback).""" + + ipn_obj = sender + + if ipn_obj.custom and ipn_obj.invoice and ipn_obj.payment_status == ST_PP_COMPLETED: + s_key, cart_pk = ipn_obj.custom.split(',') + SessionStore = import_module(settings.SESSION_ENGINE) \ + .SessionStore + session = SessionStore(s_key) + + try: + cart = Cart.objects.get(id=cart_pk) + try: + order = Order.objects.get( + transaction_id=ipn_obj.invoice) + for field in order.session_fields: + if field in session: + del session[field] + try: + del session["order"] + except KeyError: + pass + + # Since we're manually changing session data outside of + # a normal request, need to force the session object to + # save after modifying its data. + session.save() + + for item in cart: + try: + variation = ProductVariation.objects.get( + sku=item.sku) + except ProductVariation.DoesNotExist: + pass + else: + variation.update_stock(item.quantity * -1) + variation.product.actions.purchased() + + code = session.get('discount_code') + if code: + DiscountCode.objects.active().filter(code=code) \ + .update(uses_remaining=F('uses_remaining') - 1) + cart.delete() + except Order.DoesNotExist: + pass + except Cart.DoesNotExist: + pass + +payment_was_successful.connect(payment_complete) +valid_ipn_received.connect(payment_complete) diff --git a/diggersdigest/static/img/favicon.ico b/diggersdigest/static/img/favicon.ico index 8c4212f1a1c93efcc368344229ecbeb0716e6ea8..550d600ee8531545eb17aa6d73e63280c6329287 100644 GIT binary patch literal 318 zcmb`9F%Ezr5CjMC0aVt6%GgN4WBiVViQh5tfyi+b7Iq5Ry`9bFz|`-(N8V&$lvyQB zs4@)IVQa3Y+nUd1rtEMX>to}W81#zm#}rKzOxT7wHH4g`hM2Pk=X#^~>ZQOJ-O^Mi Axc~qF literal 892 zcmex=C5UDGKfoZ!!NAWTz|1Jfz$D1XEXer(2ty_V z12fQ#NPv}-3uG6P2m=ELJEs5x13M!FGeVq^iJ66!jh#bCgi{n~Aka}POmJzCq#!FB zgOD)0qM@>}i0J=Y3_L(tCP8LF273n0i?T|;^}~72eC}bMnpOBUFY)Qmm))|>Q$PH^ zdfI36y$xQxx9V=^Z-{$o#%^TyWoctj<3yH_n3r=yr-TM~OgFX+*Yg$1+G@4eZJJ8A z|N2Um$+3QsIi9)}s+B603ny&UdHy6}@}wh^=gqmrXSLg~I(ftX%~PM=j0tt!oo4aS zCEM4=xwS<-kyl^Al7y%Re>?zabnb6&cg_)LSz%H}tF z8xkDDSRPEAw_JGb)*pYv>ZjhgStP9&DLd`ce9N@WMcPLe3)fuLE(rBF)Hhi|m}lmu zYF&EfEI}r?BaY=$)$w`{Lu3h%o*1>&>*{wCaGq3Bvf44Y3B0lZgw#i23bB(t4O;*p>{rxUPa;D>Cm1tK+ zSJ46&Vd18)JKfouFMZ$g$J4AR+y2N-DUZ_ayScYkI-6`h9{#hreDc%DCzOLHMQEI` vwCkUG{>`i1b}ku9A8%Rrrs-*b6X%2#ed6=ag>T`PDicp^5ZQP<{{Kw?CXXdJ diff --git a/diggersdigest/templates/shop/includes/payment_fields.html b/diggersdigest/templates/shop/includes/payment_fields.html index 238a97d..51a5349 100644 --- a/diggersdigest/templates/shop/includes/payment_fields.html +++ b/diggersdigest/templates/shop/includes/payment_fields.html @@ -1,14 +1,16 @@ -{% load i18n mezzanine_tags %} -
- {% trans "Payment Details" %} - {% fields_for form.card_name_field %} - {% fields_for form.card_type_field %} - {% with form.card_expiry_fields as card_expiry_fields %} -
- - {% fields_for card_expiry_fields %} -
-
- {% endwith %} - {% fields_for form.card_fields %} -
+{% load i18n mezzanine_tags multipayment_forms %} + +{% if PRIMARY_PAYMENT_PROCESSOR_IN_USE %} +
+ {% trans "Payment Details" %} + {% fields_for form.card_name_field %} + {% fields_for form.card_type_field %} + {% with form.card_expiry_fields as card_expiry_fields %} +
+ + {% fields_for card_expiry_fields %} +
+ {% endwith %} + {% fields_for form.card_fields %} +
+{% endif %} diff --git a/diggersdigest/templates/shop/payment.html b/diggersdigest/templates/shop/payment.html index 6be458e..8de7e12 100644 --- a/diggersdigest/templates/shop/payment.html +++ b/diggersdigest/templates/shop/payment.html @@ -1,10 +1,54 @@ {% extends "shop/checkout.html" %} -{% load i18n mezzanine_tags %} + +{% load i18n multipayment_forms %} + + +
+
+ + +
+
+ +{% block before-form %} + {% if request.cart.has_items %} + {% multipayment_forms request form as multipayment_forms %} + {% if multipayment_forms %} +
+
+ {% for name,form in multipayment_forms %}
+ {{ form.as_p }} + +
{% endfor %} +
+
+ {% endif %} + {% endif %} +{% endblock %} {% block fields %} -{% if request.cart.has_items %} -{% errors_for form %} -{% include "shop/includes/payment_fields.html" %} -{% fields_for form.other_fields %} -{% endif %} + {% if request.cart.has_items %} + {% include "shop/includes/payment_fields.html" %} + {% if not PRIMARY_PAYMENT_PROCESSOR_IN_USE %}
{% endif %} + {{ form.other_fields.as_ul }} + {% if not PRIMARY_PAYMENT_PROCESSOR_IN_USE %}
{% endif %} + {% endif %} {% endblock %} + + +{% block nav-buttons %} + {% if request.cart.has_items %} +
+
+ {% if PRIMARY_PAYMENT_PROCESSOR_IN_USE %} + + {% endif %} + {% if not CHECKOUT_STEP_FIRST %} + + {% endif %} +
+
+ {% else %} + {{ block.super }} + {% endif %} +{% endblock %} \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 3759fc0..02e1949 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,7 +6,7 @@ dbdata: command: data only container for db db: - image: mysql + image: mysql environment: - MYSQL_ROOT_PASSWORD=mysecretpassword - MYSQL_DATABASE=diggersdigest @@ -17,10 +17,10 @@ db: web: build: . - command: python diggersdigest/manage.py runserver 0.0.0.0:8000 + command: /bin/sh diggersdigest/deploy/start_app.sh volumes: - .:/code ports: - "8000:8000" links: - - db \ No newline at end of file + - db diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 0000000..e10f4b5 --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,3 @@ + +-e git+https://github.com/Parisson/cartridge-payments.git#egg=cartridge-payments +-e git+https://github.com/Parisson/django-paypal.git#egg=django-paypal-0.2.5 diff --git a/requirements.txt b/requirements.txt index f9ca269..fe436d7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,8 +1,9 @@ -Django==1.8.3 +--index-url https://pypi.python.org/simple/ + +setuptools +Django==1.8.4 MySQL-python==1.2.5 cartridge==0.10.0 -cartridge-payments==0.97.0 -django-paypal==0.2.5 django-uuidfield==0.5.0 django-newsletter==0.5.2 parsedatetime==1.5 -- 2.39.5