git subrepo pull vendor/registripe

Also update vendored_requirements to make sure we pull this in.

subrepo:
  subdir:   "vendor/registripe"
  merged:   "9fc3645"
upstream:
  origin:   "git@gitlab.com:tchaypo/registrasion-stripe.git"
  branch:   "lca2018"
  commit:   "9fc3645"
git-subrepo:
  version:  "0.3.1"
  origin:   "???"
  commit:   "???"
This commit is contained in:
James Polley 2017-09-30 02:29:49 +10:00
parent 6eaddfc1d8
commit 660690c5cf
27 changed files with 474 additions and 233 deletions

89
vendor/registripe/.gitignore vendored Normal file
View file

@ -0,0 +1,89 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover
.hypothesis/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# IPython Notebook
.ipynb_checkpoints
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# dotenv
.env
# virtualenv
venv/
ENV/
# Spyder project settings
.spyderproject
# Rope project settings
.ropeproject

View file

@ -6,6 +6,6 @@
[subrepo]
remote = git@gitlab.com:tchaypo/registrasion-stripe.git
branch = lca2018
commit = 5ebd58154006e63d390c5feadafb9a19769c7455
parent = 5789cbd05e06742f4f7d5dfaf02c92cc8be6bfad
commit = 9fc364586b2246801979da690bdf2387b8f53e42
parent = 6eaddfc1d8dd08dfeaaf7101a827974c977f23f4
cmdver = 0.3.1

2
vendor/registripe/README.md vendored Normal file
View file

@ -0,0 +1,2 @@
# registrasion-stripe
Provides Credit Card processing for Registrasion using the Stripe API.

View file

@ -1,223 +0,0 @@
import copy
from registripe import models
from django import forms
from django.core.urlresolvers import reverse
from django.db.models import F, Q
from django.forms import widgets
from django.utils import timezone
from django_countries import countries
from django_countries.fields import LazyTypedChoiceField
from django_countries.widgets import CountrySelectWidget
class NoRenderWidget(forms.widgets.HiddenInput):
def render(self, name, value, attrs=None):
return "<!-- no widget: " + name + " -->"
def secure_striped(field):
''' Calls stripe() with secure=True. '''
return striped(field, True)
def striped(field, secure=False):
oldwidget = field.widget
field.widget = StripeWidgetProxy(oldwidget, secure)
return field
class StripeWidgetProxy(widgets.Widget):
def __init__(self, underlying, secure=False):
self.underlying = underlying
self.secure = secure
def __deepcopy__(self, memo):
copy_underlying = copy.deepcopy(self.underlying, memo)
return type(self)(copy_underlying, self.secure)
def __getattribute__(self, attr):
spr = super(StripeWidgetProxy, self).__getattribute__
if attr in ("underlying", "render", "secure", "__deepcopy__"):
return spr(attr)
else:
return getattr(self.underlying, attr)
def render(self, name, value, attrs=None):
if not attrs:
attrs = {}
attrs["data-stripe"] = name
if self.secure:
name = ""
return self.underlying.render(name, value, attrs=attrs)
class CreditCardForm(forms.Form):
required_css_class = 'label-required'
def _media(self):
js = (
'https://js.stripe.com/v2/',
reverse("registripe_pubkey"),
)
return forms.Media(js=js)
media = property(_media)
number = secure_striped(forms.CharField(
required=False,
label="Credit card Number",
help_text="Your credit card number, with or without spaces.",
max_length=255,
))
exp_month = secure_striped(forms.IntegerField(
required=False,
label="Card expiry month",
min_value=1,
max_value=12,
))
exp_year = secure_striped(forms.IntegerField(
required=False,
label="Card expiry year",
help_text="The expiry year for your card in 4-digit form",
min_value=timezone.now().year,
))
cvc = secure_striped(forms.CharField(
required=False,
min_length=3,
max_length=4,
))
stripe_token = forms.CharField(
max_length=255,
# required=True,
widget=NoRenderWidget(),
)
name = striped(forms.CharField(
required=True,
label="Cardholder name",
help_text="The cardholder's name, as it appears on the credit card",
max_length=255,
))
address_line1 = striped(forms.CharField(
required=True,
label="Cardholder account address, line 1",
max_length=255,
))
address_line2 = striped(forms.CharField(
required=False,
label="Cardholder account address, line 2",
max_length=255,
))
address_city = striped(forms.CharField(
required=True,
label="Cardholder account city",
max_length=255,
))
address_state = striped(forms.CharField(
required=True,
max_length=255,
label="Cardholder account state or province",
))
address_zip = striped(forms.CharField(
required=True,
max_length=255,
label="Cardholder account postal code",
))
address_country = striped(LazyTypedChoiceField(
label="Cardholder account country",
choices=countries,
widget=CountrySelectWidget,
))
class StripeRefundForm(forms.Form):
required_css_class = 'label-required'
def __init__(self, *args, **kwargs):
'''
Arguments:
user (User): The user whose charges we should filter to.
min_value (Decimal): The minimum value of the charges we should
show (currently, credit notes can only be cashed out in full.)
'''
user = kwargs.pop('user', None)
min_value = kwargs.pop('min_value', None)
super(StripeRefundForm, self).__init__(*args, **kwargs)
payment_field = self.fields['payment']
qs = payment_field.queryset
if user:
qs = qs.filter(
charge__customer__user=user,
)
if min_value is not None:
# amount >= amount_to_refund + amount_refunded
# No refunds yet
q1 = (
Q(charge__amount_refunded__isnull=True) &
Q(charge__amount__gte=min_value)
)
# There are some refunds
q2 = (
Q(charge__amount_refunded__isnull=False) &
Q(charge__amount__gte=(
F("charge__amount_refunded") + min_value))
)
qs = qs.filter(q1 | q2)
payment_field.queryset = qs
payment = forms.ModelChoiceField(
required=True,
queryset=models.StripePayment.objects.all(),
)
'''{
From stripe.js details:
Card details:
The first argument to createToken is a JavaScript object containing credit
card data entered by the user. It should contain the following required
members:
number: card number as a string without any separators
(e.g., "4242424242424242")
exp_month: two digit number representing the card's expiration month
(e.g., 12)
exp_year: two or four digit number representing the card's expiration year
(e.g., 2017)
(The expiration date can also be passed as a single string.)
cvc: optional, but we highly recommend you provide it to help prevent fraud.
This is the card's security code, as a string (e.g., "123").
The following fields are entirely optional and cannot result in a token
creation failure:
name: cardholder name
address_line1: billing address line 1
address_line2: billing address line 2
address_city: billing address city
address_state: billing address state
address_zip: billing postal code as a string (e.g., "94301")
address_country: billing address country
}
'''

3
vendor/registripe/registripe/admin.py vendored Normal file
View file

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

120
vendor/registripe/registripe/forms.py vendored Normal file
View file

@ -0,0 +1,120 @@
import copy
from registripe import models
from django import forms
from django.core.urlresolvers import reverse
import functools
from django import forms
from django.core.urlresolvers import reverse
from django.core.exceptions import ValidationError
from django.db.models import F, Q
from django.forms import widgets
from django.utils import timezone
from django_countries import countries
from django_countries.fields import LazyTypedChoiceField
from django_countries.widgets import CountrySelectWidget
from pinax.stripe import models as pinax_stripe_models
class StripeCardElement(forms.widgets.TextInput):
def render(self, name, value, attrs=None):
element = '''
<div class="registrasion-stripe-element" id='%s' style='"-moz-appearance: textfield; -webkit-appearance: textfield; appearance: field;"'>Please wait.</div>''' % (name, )
script = '''
<script type='text/javascript'>
window.addEventListener('load', function(event){
stripeify('%s');
});
</script>''' % (name)
return element + script
class StripeTokenWidget(forms.widgets.HiddenInput):
def render(self, name, value, attrs=None):
return '''
<div class='registrasion-stripe-token' style='display:none;'
data-input-id='%s'
></div>
''' % (name, )
class CreditCardForm(forms.Form):
required_css_class = 'label-required'
def _media(self):
js = (
'https://js.stripe.com/v3/',
reverse("registripe_form_handler"),
)
return forms.Media(js=js)
media = property(_media)
card = forms.CharField(
required=False,
label="Credit card",
max_length=255,
widget=StripeCardElement()
)
stripe_token = forms.CharField(
max_length=255,
#required=True,
widget=StripeTokenWidget(),
)
class StripeRefundForm(forms.Form):
def __init__(self, *args, **kwargs):
'''
Arguments:
user (User): The user whose charges we should filter to.
min_value (Decimal): The minimum value of the charges we should
show (currently, credit notes can only be cashed out in full.)
'''
user = kwargs.pop('user', None)
min_value = kwargs.pop('min_value', None)
super(StripeRefundForm, self).__init__(*args, **kwargs)
payment_field = self.fields['payment']
qs = payment_field.queryset
if user:
qs = qs.filter(
charge__customer__user=user,
)
if min_value is not None:
# amount >= amount_to_refund + amount_refunded
# No refunds yet
q1 = (
Q(charge__amount_refunded__isnull=True) &
Q(charge__amount__gte=min_value)
)
# There are some refunds
q2 = (
Q(charge__amount_refunded__isnull=False) &
Q(charge__amount__gte=(
F("charge__amount_refunded") + min_value)
)
)
qs = qs.filter(q1 | q2)
payment_field.queryset = qs
payment = forms.ModelChoiceField(
required=True,
queryset=models.StripePayment.objects.all(),
)

View file

@ -9,7 +9,6 @@ class StripePayment(commerce.PaymentBase):
charge = models.ForeignKey(Charge)
class StripeCreditNoteRefund(commerce.CreditNoteRefund):
charge = models.ForeignKey(Charge)

View file

@ -0,0 +1,13 @@
{% extends "registrasion/stripe/credit_card_payment_.html" %}
{% comment %}
Blocks that you can override:
- balance_due
You MUST include the following in your template:
- {{ form.media.js }}
- the payment form, where the form's id="payent-form"
- there is a div with id="payment-errors"
{% endcomment %}

View file

@ -0,0 +1,55 @@
{% extends "registrasion/base.html" %}
{% load registrasion_tags %}
{% block title %}Credit card payment for invoice #{{ invoice.id}}{% endblock %}
{% block heading %}Credit card payment for invoice #{{ invoice.id}}{% endblock %}
{% block lede %}
Credit card payments are processed by <a href="https://stripe.com">Stripe</a>.
We do not store any of your credit card data locally, but instead Stripe will securely tokenise your card details. To allow this, you must allow JavaScript from <code>js.stripe.com</code>.
{% endblock %}
{% block content %}
{% block balance_due_outer %}
<div class="alert alert-info">
<p>
{% block balance_due %}
You have ${{ invoice.balance_due }} remaining to pay on this invoice.
{% endblock %}
</p>
</div>
{% endblock %}
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">Card details</h3>
</div>
<form id="payment-form" method="post">
<div class="panel-body">
{% csrf_token %}
{% include "registrasion/form.html" %}
</div>
<div class="panel-footer">
<input id="pay" class="btn btn-primary" type="submit" value="Pay {{ invoice.balance_due }}" />
<a class="btn btn-default" href='{% url "invoice" invoice.id invoice.user.attendee.access_code %}'>Return to invoice</a>
</div>
</form>
</div>
{% block form_scripts %}
{% block add_form_control_class_script %}
<script>
elements = document.getElementsByClassName("registrasion-stripe-element");
Array.prototype.forEach.call(elements, function(element) {
element.setAttribute("class", "{% block form_control_class %}form-control{% endblock %}");
});
</script>
{% endblock %}
{{ form.media.js }}
{% endblock %}
{% endblock %}

View file

@ -0,0 +1,68 @@
var stripe = Stripe('{{ PINAX_STRIPE_PUBLIC_KEY }}');
var elements = stripe.elements();
function stripeify(elementId) {
var element = elements.create(elementId);
element.mount('#' + elementId);
var htmlElement = document.getElementById(elementId);
var errors = elementId + "-errors";
htmlElement.insertAdjacentHTML("afterend", "<div id='" + errors + "' role='alert' class='help-block'></div>");
var displayError = document.getElementById(errors);
//Handle real-time validation errors from the card Element.
element.addEventListener('change', function(event) {
toggleErrorMessage(displayError, event.error);
});
// Create a token or display an error when the form is submitted.
var paymentForm = document.getElementById('payment-form');
paymentForm.addEventListener('submit', function(event) {
event.preventDefault();
stripe.createToken(element).then(function(result) {
if (result.error) {
// Inform the user if there was an error
toggleErrorMessage(displayError, result.error);
} else {
// Send the token to your server
stripeTokenHandler(result.token);
}
});
});
}
function toggleErrorMessage(errorElement, maybeError) {
errorClass = inputErrorClassName();
if (maybeError) {
errorElement.textContent = maybeError.message;
errorElement.parentNode.classList.add(errorClass);
} else {
errorElement.textContent = '';
errorElement.parentNode.classList.remove(errorClass);
}
}
function inputErrorClassName() {
return {% block form_control_error_class %}"has-error"{% endblock %};
}
function stripeTokenHandler(token) {
// Insert the token ID into the form so it gets submitted to the server
var form = document.getElementById('payment-form');
tokenHolder = form.getElementsByClassName('registrasion-stripe-token')[0];
inputId = tokenHolder.dataset.inputId;
var hiddenInput = document.createElement('input');
hiddenInput.setAttribute('type', 'hidden');
hiddenInput.setAttribute('name', inputId);
hiddenInput.setAttribute('value', token.id);
tokenHolder.appendChild(hiddenInput);
// Submit the form
form.submit();
}

View file

@ -0,0 +1,6 @@
{% extends "registrasion/stripe/link_to_payment_.html" %}
{% comment %}
Blocks that you can override:
{% endcomment %}

View file

@ -0,0 +1,7 @@
{% comment %}
This is used in the default credit_note.html file to display Stripe funcationality if the app is loaded.
{% endcomment %}
{% block content %}
<a class="btn btn-primary" href='{% url "registripe_card" invoice_id access_code %}'>Pay this invoice with Stripe</a>
{% endblock %}

View file

@ -0,0 +1,6 @@
{% extends "registrasion/stripe/link_to_refunds_.html" %}
{% comment %}
Blocks that you can override:
{% endcomment %}

View file

@ -0,0 +1,9 @@
{% comment %}
This is used in the default invoice.html file to display Stripe funcationality if the app is loaded.
{% endcomment %}
{% block content %}
<h3>Stripe Refund</h3>
<p><a href="{% url 'registripe_refund' credit_note_id %}">View Stripe refund options</a></p>
{% endblock %}

View file

@ -0,0 +1,6 @@
{% extends "registrasion/stripe/refund_.html" %}
{% comment %}
Blocks that you can override:
{% endcomment %}

View file

@ -0,0 +1,24 @@
{% extends "registrasion/base.html" %}
{% load registrasion_tags %}
{% block title %}Stripe refunds for credit note #{{ credit_note.id}}{% endblock %}
{% block heading %}Stripe refunds for credit note #{{ credit_note.id}}{% endblock %}
{% block lede %}
Currently credit notes can only be cashed out in full in a single
transaction. If you need to cash out to multiple small payments, you will
need to manually invoke the refunds from the Stripe Dashboard.
{% endblock %}
{% block content %}
<p>This credit note is valued at ${{ credit_note.value }}.</p>
<h3>Available refunds</h3>
<form method="post">
{% csrf_token %}
{% include "registrasion/form.html" %}
<input id="submit" class="btn btn-primary" type="submit" value="Refund {{ credit_note.value }} to payment source" />
</form>
{% endblock %}

3
vendor/registripe/registripe/tests.py vendored Normal file
View file

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

View file

@ -7,9 +7,8 @@ from pinax.stripe.views import Webhook
urlpatterns = [
url(r"^card/([0-9]*)/$", views.card, name="registripe_card"),
url(r"^card/([0-9]*)/([0-9A-Za-z]*)/$", views.card,
name="registripe_card"),
url(r"^pubkey/$", views.pubkey_script, name="registripe_pubkey"),
url(r"^card/([0-9]*)/([0-9A-Za-z]*)/$", views.card, name="registripe_card"),
url(r"^form_handler.js", views.form_handler, name="registripe_form_handler"),
url(r"^refund/([0-9]*)/$", views.refund, name="registripe_refund"),
url(r"^webhook/$", Webhook.as_view(), name="pinax_stripe_webhook"),
]

View file

@ -1,5 +1,7 @@
from registripe import forms
from registripe import models
import forms
import models
from django.core.exceptions import ValidationError
from django.conf import settings
@ -14,6 +16,8 @@ from registrasion.controllers.credit_note import CreditNoteController
from registrasion.controllers.invoice import InvoiceController
from pinax.stripe import actions
from pinax.stripe.actions import refunds as pinax_stripe_actions_refunds
from registrasion.models import commerce
from stripe.error import StripeError
@ -28,8 +32,19 @@ def _staff_only(user):
return user.is_staff
def pubkey_script(request):
''' Returns a JS snippet that sets the Stripe public key for Stripe.js. '''
def form_handler(request):
''' Renders a js file to process Stripe payments. '''
data = {
"PINAX_STRIPE_PUBLIC_KEY": settings.PINAX_STRIPE_PUBLIC_KEY,
}
return render(
request,
"registrasion/stripe/js/form_handler.js",
data,
content_type="text/javascript",
)
script_template = "Stripe.setPublishableKey('%s');"
script = script_template % settings.PINAX_STRIPE_PUBLIC_KEY
@ -65,7 +80,7 @@ def card(request, invoice_id, access_code=None):
if request.POST and form.is_valid():
try:
inv.validate_allowed_to_pay()
inv.validate_allowed_to_pay() # Verify that we're allowed to do this.
process_card(request, form, inv)
return to_invoice
except StripeError as e:
@ -185,7 +200,7 @@ def process_refund(cn, form):
"the credit note."
)
refund = actions.refunds.create(charge, to_refund) # noqa
refund = actions.refunds.create(charge, to_refund)
models.StripeCreditNoteRefund.objects.create(
parent=cn.credit_note,

4
vendor/registripe/requirements.txt vendored Normal file
View file

@ -0,0 +1,4 @@
django-countries==4.0
pinax-stripe==3.2.1
requests>=2.11.1
stripe==1.38.0

35
vendor/registripe/setup.py vendored Normal file
View file

@ -0,0 +1,35 @@
#!/usr/bin/env python
import os
from setuptools import setup, find_packages
import registripe
def read_file(filename):
"""Read a file into a string."""
path = os.path.abspath(os.path.dirname(__file__))
filepath = os.path.join(path, filename)
try:
return open(filepath).read()
except IOError:
return ''
setup(
name="registrasion-stripe",
author="Christopher Neugebauer",
author_email="_@chrisjrn.com",
version=registripe.__version__,
description="Stripe-based payments for the Registrasion conference registration package.",
url="http://github.com/chrisjrn/registrasion-stripe/",
packages=find_packages(),
include_package_data=True,
classifiers=(
"Development Status :: 3 - Alpha",
"Programming Language :: Python",
"Framework :: Django",
"Intended Audience :: Developers",
"Natural Language :: English",
"License :: OSI Approved :: Apache Software License",
),
install_requires=read_file("requirements.txt").splitlines(),
)

View file

@ -1 +1,2 @@
vendor/registrasion
vendor/registripe