Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Added stripe gateway #22

Merged
merged 34 commits into from

2 participants

@hayyat

No description provided.

@theju

In the tests, please create a plan if doesn't exist. This makes it easy for people who are running the tests.

@theju

Please use the standard 'FAILURE' to denote the failure of a transaction. Refer docs at http://agiliq.com/static/docs/merchant/gateways.html

@theju
Collaborator

Stripe has authorize and capture method as well. Please check https://stripe.com/docs/api?lang=python#create_token

@theju

Additional kwarg options is missing. Please add that to the method signature.

@theju
Collaborator

Please add an index link to the stripe gateway in the docs so that it can be easily accessible.

@theju theju merged commit 8ea9552 into agiliq:master
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Dec 22, 2011
  1. @hayyat
  2. @hayyat

    added stripe_gateway file

    hayyat authored
Commits on Dec 23, 2011
  1. @hayyat
  2. @hayyat
  3. @hayyat
Commits on Dec 25, 2011
  1. @hayyat
  2. @hayyat
  3. @hayyat
Commits on Dec 26, 2011
  1. @hayyat

    pyflaked stripe gateway file

    hayyat authored
  2. @hayyat

    added tests for stripe gateway

    hayyat authored
  3. @hayyat

    added plaban to contributors

    hayyat authored
  4. @hayyat

    added stripe tests

    hayyat authored
  5. @hayyat
  6. @hayyat

    slightly modified credit method

    hayyat authored
Commits on Dec 27, 2011
  1. @hayyat

    added docs for stripe gateway

    hayyat authored
  2. @hayyat
Commits on Dec 28, 2011
  1. @hayyat

    added capture method of samurai

    hayyat authored
  2. @hayyat
  3. @hayyat

    debugged a mistake in docs

    hayyat authored
  4. @hayyat
  5. @hayyat

    pep8fied some files

    hayyat authored
  6. @hayyat

    pyflaked some files

    hayyat authored
Commits on Dec 29, 2011
  1. @hayyat
  2. @hayyat

    added more methods to samurai

    hayyat authored
Commits on Dec 30, 2011
  1. @hayyat
  2. @hayyat
  3. @hayyat

    changed samurai docs

    hayyat authored
  4. @hayyat

    changed stripe docs

    hayyat authored
  5. @hayyat

    updated docs index

    hayyat authored
  6. @hayyat

    added samurai to app

    hayyat authored
Commits on Jan 2, 2012
  1. @hayyat

    added samurai integration

    hayyat authored
  2. @hayyat
  3. @hayyat

    modified samurai docs

    hayyat authored
  4. @hayyat

    debugged docs

    hayyat authored
This page is out of date. Refresh to see the latest.
Showing with 962 additions and 27 deletions.
  1. +5 −1 .gitignore
  2. +1 −0  CONTRIBUTORS.txt
  3. +8 −0 billing/forms/samurai_forms.py
  4. +8 −0 billing/forms/stripe_forms.py
  5. +11 −12 billing/gateway.py
  6. +95 −0 billing/gateways/samurai_gateway.py
  7. +118 −0 billing/gateways/stripe_gateway.py
  8. +27 −0 billing/integrations/samurai_integration.py
  9. +29 −0 billing/integrations/stripe_integration.py
  10. +6 −0 billing/templates/billing/samurai.html
  11. +6 −0 billing/templates/billing/stripe.html
  12. +4 −1 billing/templatetags/billing_tags.py
  13. +24 −0 billing/templatetags/samurai_tags.py
  14. +24 −0 billing/templatetags/stripe_tags.py
  15. +2 −0  billing/tests/__init__.py
  16. +58 −0 billing/tests/samurai_tests.py
  17. +78 −0 billing/tests/stripe_tests.py
  18. +68 −0 docs/gateways/samurai_gateway.rst
  19. +63 −0 docs/gateways/stripe_payment.rst
  20. +4 −0 docs/index.rst
  21. +68 −0 docs/offsite/samurai_integration.rst
  22. +69 −0 docs/offsite/stripe_integration.rst
  23. +2 −4 example/app/forms.py
  24. +5 −1 example/app/templates/app/base.html
  25. +50 −0 example/app/templates/app/samurai.html
  26. +54 −0 example/app/templates/app/stripe.html
  27. +16 −4 example/app/urls.py
  28. +50 −4 example/app/views.py
  29. +7 −0 example/localsettings.example.py
  30. +2 −0  example/settings.py
View
6 .gitignore
@@ -4,10 +4,14 @@
src
tmp
*.db
-
+*.*~
+#*#
example/tmp
example/localsettings.py
docs/_build/*
*_flymake.py*
+#*
+.py#
+
View
1  CONTRIBUTORS.txt
@@ -10,3 +10,4 @@ Thanks go out to all the contributors listed in alphabetical order:
* Markus Gattol <markus.gattol@sunoano.org>
* Shabda Raaj <shabda@agiliq.com>
* Thejaswi Puthraya <thejaswi.puthraya@gmail.com>
+* Plaban Nayak <plaban.nayak@gmail.com>
View
8 billing/forms/samurai_forms.py
@@ -0,0 +1,8 @@
+from django import forms
+
+
+class SamuraiForm(forms.Form):
+ credit_card__number = forms.CharField()
+ credit_card__cvc = forms.CharField(max_length=4)
+ credit_card__expiration_month = forms.CharField(max_length=7)
+ credit_card__expiration_year = forms.CharField(max_length=7)
View
8 billing/forms/stripe_forms.py
@@ -0,0 +1,8 @@
+from django import forms
+
+
+class StripeForm(forms.Form):
+ credit_card__number = forms.CharField()
+ credit_card__cvc = forms.CharField(max_length=4)
+ credit_card__expiration_month = forms.CharField(max_length=7)
+ credit_card__expiration_year = forms.CharField(max_length=7)
View
23 billing/gateway.py
@@ -28,7 +28,7 @@ class Gateway(object):
# Sequence of countries supported by the gateway in ISO 3166 alpha-2 format.
# http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
supported_countries = []
- # Sequence of supported card types by the gateway. Members should be valid
+ # Sequence of supported card types by the gateway. Members should be valid
# subclasses of the Credit Card object.
supported_cardtypes = []
# Home page URL for the gateway. Used for information purposes only.
@@ -55,39 +55,39 @@ def validate_card(self, credit_card):
# Gateways might provide some random number which
# might not pass Luhn's test.
if self.test_mode:
- return True
+ return True
return credit_card.is_valid()
- def purchase(self, money, credit_card, options = None):
+ def purchase(self, money, credit_card, options=None):
"""One go authorize and capture transaction"""
raise NotImplementedError
- def authorize(self, money, credit_card, options = None):
+ def authorize(self, money, credit_card, options=None):
"""Authorization for a future capture transaction"""
raise NotImplementedError
- def capture(self, money, authorization, options = None):
+ def capture(self, money, authorization, options=None):
"""Capture funds from a previously authorized transaction"""
raise NotImplementedError
- def void(self, identification, options = None):
+ def void(self, identification, options=None):
"""Null/Blank/Delete a previous transaction"""
raise NotImplementedError
- def credit(self, money, identification, options = None):
+ def credit(self, money, identification, options=None):
"""Refund a previously 'settled' transaction"""
raise NotImplementedError
- def recurring(self, money, creditcard, options = None):
+ def recurring(self, money, creditcard, options=None):
"""Setup a recurring transaction"""
raise NotImplementedError
- def store(self, creditcard, options = None):
+ def store(self, creditcard, options=None):
"""Store the credit card and user profile information
on the gateway for future use"""
raise NotImplementedError
- def unstore(self, identification, options = None):
+ def unstore(self, identification, options=None):
"""Delete the previously stored credit card and user
profile information on the gateway"""
raise NotImplementedError
@@ -98,14 +98,13 @@ def get_gateway(gateway, *args, **kwargs):
This caches gateway classes in a module-level dictionnary to avoid hitting
the filesystem every time we require a gateway.
- Should the list of available gateways change at runtime, one should then
+ Should the list of available gateways change at runtime, one should then
invalidate the cache, the simplest of ways would be to:
>>> gateway_cache = {}
"""
# Is the class in the cache?
clazz = gateway_cache.get(gateway, None)
-
if not clazz:
# Let's actually load it (it's not in the cache)
gateway_filename = "%s_gateway" %gateway
View
95 billing/gateways/samurai_gateway.py
@@ -0,0 +1,95 @@
+from billing import Gateway
+from billing.utils.credit_card import InvalidCard, Visa, MasterCard, \
+ AmericanExpress, Discover
+import samurai
+from django.conf import settings
+
+
+class SamuraiGateway(Gateway):
+ supported_cardtypes = [Visa, MasterCard, AmericanExpress, Discover]
+ supported_countries = ['US']
+ default_currency = "USD"
+ homepage_url = "https://samurai.feefighters.com/"
+ display_name = "Samurai"
+
+ def __init__(self):
+ import samurai.config as config
+ config.merchant_key = settings.SAMURAI_MERCHANT_KEY
+ config.merchant_password = settings.SAMURAI_MERCHANT_PASSWORD
+ config.processor_token = settings.SAMURAI_PROCESSOR_TOKEN
+ self.samurai = samurai
+
+ def purchase(self, money, credit_card):
+ if not self.validate_card(credit_card):
+ raise InvalidCard("Invalid Card")
+ try:
+ from samurai.payment_method import PaymentMethod
+ from samurai.processor import Processor
+ pm = PaymentMethod.create(credit_card.number, credit_card.verification_value,
+ credit_card.month, credit_card.year)
+ payment_method_token = pm.payment_method_token
+ response = Processor.purchase(payment_method_token, money)
+ except Exception, error:
+ return {'status': 'FAILURE', 'response': error}
+ return {'status': 'SUCCESS', 'response': response}
+
+ def authorize(self, money, credit_card, options=None):
+ if not self.validate_card(credit_card):
+ raise InvalidCard("Invalid Card")
+ try:
+ from samurai.payment_method import PaymentMethod
+ from samurai.processor import Processor
+ pm = PaymentMethod.create(credit_card.number, credit_card.verification_value, credit_card.month, credit_card.year)
+ payment_method_token = pm.payment_method_token
+ response = Processor.authorize(payment_method_token, money)
+ except Exception, error:
+ return {'status': 'FAILURE', 'response': error}
+ return {'status': 'SUCCESS', 'response': response}
+
+ def capture(self, money, identification, options=None):
+ from samurai.transaction import Transaction
+ trans = Transaction.find(identification)
+ if not trans.errors:
+ new_trans = trans.capture(money)
+ return{'status': "SUCCESS", "response": new_trans}
+ else:
+ return{"status": "FAILURE", "response": trans.errors}
+
+ def reverse(self, money, identification, options=None):
+ from samurai.transaction import Transaction
+ trans = Transaction.find(identification)
+ if not trans.errors:
+ new_trans = trans.reverse(money)
+ return{'status': "SUCCESS", "response": new_trans}
+ else:
+ return{"status": "FAILURE", "response": trans.errors}
+
+ def void(self, identification, options=None):
+ from samurai.transaction import Transaction
+ trans = Transaction.find(identification)
+ if not trans.errors:
+ new_trans = trans.void()
+ return{'status': "SUCCESS", "response": new_trans}
+ else:
+ return{"status": "FAILURE", "response": trans.errors}
+
+ def credit(self, money, identification, options=None):
+ from samurai.transaction import Transaction
+ trans = Transaction.find(identification)
+ if not trans.errors:
+ new_trans = trans.credit(money)
+ return{'status': "SUCCESS", "response": new_trans}
+ else:
+ return{"status": "FAILURE", "response": trans.errors}
+
+ def store(self, credit_card, options=None):
+ from samurai.payment_method import PaymentMethod
+ pm = PaymentMethod.create(credit_card.number, credit_card.verification_value, credit_card.month, credit_card.year)
+ response = pm.retain()
+ return {'status': 'SUCCESS', 'response': response}
+
+ def unstore(self, identification, options=None):
+ from samurai.payment_method import PaymentMethod
+ payment_method = PaymentMethod.find(identification)
+ payment_method = payment_method.redact()
+ return {"status": "SUCCESS", "response": payment_method}
View
118 billing/gateways/stripe_gateway.py
@@ -0,0 +1,118 @@
+from billing import Gateway
+from billing.utils.credit_card import InvalidCard, Visa, MasterCard, \
+ AmericanExpress, Discover
+import stripe
+from django.conf import settings
+
+
+class StripeGateway(Gateway):
+ supported_cardtypes = [Visa, MasterCard, AmericanExpress, Discover]
+ supported_countries = ['US']
+ default_currency = "USD"
+ homepage_url = "https://stripe.com/"
+ display_name = "Stripe"
+
+ def __init__(self):
+ stripe.api_key = settings.STRIPE_API_KEY
+ self.stripe = stripe
+
+ def purchase(self, amount, credit_card, options=None):
+ if not self.validate_card(credit_card):
+ raise InvalidCard("Invalid Card")
+ try:
+ response = self.stripe.Charge.create(
+ amount=amount * 100,
+ currency=self.default_currency.lower(),
+ card={
+ 'number': credit_card.number,
+ 'exp_month': credit_card.month,
+ 'exp_year': credit_card.year,
+ 'cvc': credit_card.verification_value
+ },)
+ except self.stripe.CardError, error:
+ return {'status': 'FAILURE', 'response': error}
+ return {'status': 'SUCCESS', 'response': response}
+
+ def store(self, credit_card, options=None):
+ if not self.validate_card(credit_card):
+ raise InvalidCard("Invalid Card")
+ customer = self.stripe.Customer.create(
+ card={
+ 'number': credit_card.number,
+ 'exp_month': credit_card.month,
+ 'exp_year': credit_card.year,
+ 'cvc': credit_card.verification_value
+ },
+ description="Storing for future use"
+ )
+ return {'status': 'SUCCESS', 'response': customer}
+
+ def recurring(self, credit_card, options=None):
+ if not self.validate_card(credit_card):
+ raise InvalidCard("Invalid Card")
+ response = None
+ try:
+ plan_id = options['plan_id']
+ self.stripe.Plan.retrieve(options['plan_id'])
+ try:
+ response = self.stripe.Customer.create(
+ card={
+ 'number': credit_card.number,
+ 'exp_month': credit_card.month,
+ 'exp_year': credit_card.year,
+ 'cvc': credit_card.verification_value
+ },
+ plan=plan_id,
+ description="Thanks for subscribing"
+ )
+ return {"status": "SUCCESS", "response": response}
+ except self.stripe.CardError, error:
+ return {"status": "FAILURE", "response": error}
+ except self.stripe.InvalidRequestError, error:
+ return {"status": "FAILURE", "response": error}
+ except TypeError:
+ return {"status": "FAILURE", "response": "please give a plan id"}
+
+ def unstore(self, identification, options=None):
+ try:
+ customer = self.stripe.Customer.retrieve(identification)
+ response = customer.delete()
+ return {"status": "SUCCESS", "response": response}
+ except self.stripe.InvalidRequestError, error:
+ return {"status": "FAILURE", "response": error}
+
+ def credit(self, identification, money=None, options=None):
+ try:
+ charge = self.stripe.Charge.retrieve(identification)
+ response = charge.refund(amount=money)
+ return {"status": "SUCCESS", "response": response}
+ except self.stripe.InvalidRequestError, error:
+ return {"status": "FAILURE", "error": error}
+
+ def authorize(self, money, credit_card, options=None):
+ if not self.validate_card(credit_card):
+ raise InvalidCard("Invalid Card")
+ try:
+ token = self.stripe.Token.create(
+ card={
+ 'number': credit_card.number,
+ 'exp_month': credit_card.month,
+ 'exp_year': credit_card.year,
+ 'cvc': credit_card.verification_value
+ },
+ amount=money * 100
+ )
+ return {'status': "SUCCESS", "response": token}
+ except self.stripe.InvalidRequestError, error:
+ return {"status": "FAILURE", "response": error}
+
+ def capture(self, money, authorization, options=None):
+ try:
+ response = self.stripe.Charge.create(
+ amount=money * 100,
+ card=authorization,
+ currency="usd"
+ )
+ return {'status': "SUCCESS", "response": response}
+ except self.stripe.InvalidRequestError, error:
+ return {"status": "FAILURE", "response": error}
View
27 billing/integrations/samurai_integration.py
@@ -0,0 +1,27 @@
+from billing import Integration
+from django.conf import settings
+from django.conf.urls.defaults import patterns, url
+from django.views.decorators.csrf import csrf_exempt
+from django.http import HttpResponse
+from billing.forms.samurai_forms import SamuraiForm
+
+
+class SamuraiIntegration(Integration):
+ def __init__(self):
+ super(SamuraiIntegration, self).__init__()
+
+ def generate_form(self):
+ initial_data = self.fields
+ form = SamuraiForm(initial=initial_data)
+ return form
+
+
+ @csrf_exempt
+ def get_token(self, request):
+ token = request.POST['samuraiToken']
+ return HttpResponse('Success')
+
+ def get_urls(self):
+ urlpatterns = patterns('',
+ url('^samurai-get-token/$', self.get_token, name="get_token"),)
+ return urlpatterns
View
29 billing/integrations/stripe_integration.py
@@ -0,0 +1,29 @@
+from billing import Integration
+from django.conf import settings
+from django.conf.urls.defaults import patterns, url
+import stripe
+from billing.forms.stripe_forms import StripeForm
+from django.views.decorators.csrf import csrf_exempt
+from django.http import HttpResponse
+
+
+class StripeIntegration(Integration):
+ def __init__(self):
+ super(StripeIntegration, self).__init__()
+ stripe.api_key = settings.STRIPE_API_KEY
+ self.stripe = stripe
+
+ def generate_form(self):
+ initial_data = self.fields
+ form = StripeForm(initial=initial_data)
+ return form
+
+ @csrf_exempt
+ def get_token(self, request):
+ token = request.POST['stripeToken']
+ return HttpResponse('Success')
+
+ def get_urls(self):
+ urlpatterns = patterns('',
+ url('^stripe-get-token/$', self.get_token, name="get_token"),)
+ return urlpatterns
View
6 billing/templates/billing/samurai.html
@@ -0,0 +1,6 @@
+<div class="payment-errors"> </div>
+<form method="post" id="payment-form">{% csrf_token %}
+
+ {{ integration.generate_form.as_p }}
+<input type="submit" value="Pay" class="submit-button"/>
+</form>
View
6 billing/templates/billing/stripe.html
@@ -0,0 +1,6 @@
+<div class="payment-errors"> </div>
+<form method="post" id="payment-form">{% csrf_token %}
+
+ {{ integration.generate_form.as_p }}
+<input type="submit" value="Pay" class="submit-button"/>
+</form>
View
5 billing/templatetags/billing_tags.py
@@ -7,7 +7,8 @@
from billing.templatetags.google_checkout_tags import google_checkout
from billing.templatetags.amazon_fps_tags import amazon_fps
from billing.templatetags.braintree_payments_tags import braintree_payments
-
+from billing.templatetags.stripe_tags import stripe_payment
+from billing.templatetags.samurai_tags import samurai_payment
register = template.Library()
register.tag(google_checkout)
@@ -15,3 +16,5 @@
register.tag(world_pay)
register.tag(amazon_fps)
register.tag(braintree_payments)
+register.tag(stripe_payment)
+register.tag(samurai_payment)
View
24 billing/templatetags/samurai_tags.py
@@ -0,0 +1,24 @@
+'''
+Template tags for Samurai Non PCI Complaince
+'''
+from django import template
+from django.template.loader import render_to_string
+from billing.forms.samurai_forms import SamuraiForm
+
+class SamuraiNode(template.Node):
+ def __init__(self, integration):
+ self.integration = template.Variable(integration)
+
+ def render(self, context):
+ int_obj = self.integration.resolve(context)
+ form_str = render_to_string("billing/samurai.html",
+ {"form":SamuraiForm(int_obj),
+ "integration": int_obj}, context)
+ return form_str
+
+def samurai_payment(parser, token):
+ try:
+ tag, int_obj = token.split_contents()
+ except ValueError:
+ raise template.TemplateSyntaxError("%r was expecting a single argument" %token.split_contents()[0])
+ return SamuraiNode(int_obj)
View
24 billing/templatetags/stripe_tags.py
@@ -0,0 +1,24 @@
+'''
+Template tags for Stripe Non PCI Complaince
+'''
+from django import template
+from django.template.loader import render_to_string
+from billing.forms.stripe_forms import StripeForm
+
+class StripeNode(template.Node):
+ def __init__(self, integration):
+ self.integration = template.Variable(integration)
+
+ def render(self, context):
+ int_obj = self.integration.resolve(context)
+ form_str = render_to_string("billing/stripe.html",
+ {"form":StripeForm(int_obj),
+ "integration": int_obj}, context)
+ return form_str
+
+def stripe_payment(parser, token):
+ try:
+ tag, int_obj = token.split_contents()
+ except ValueError:
+ raise template.TemplateSyntaxError("%r was expecting a single argument" %token.split_contents()[0])
+ return StripeNode(int_obj)
View
2  billing/tests/__init__.py
@@ -8,6 +8,8 @@
from amazon_fps_tests import *
from braintree_payments_tests import *
from braintree_payments_tr_tests import *
+from stripe_tests import *
+from samurai_tests import *
if __name__ == "__main__":
unittest.main()
View
58 billing/tests/samurai_tests.py
@@ -0,0 +1,58 @@
+from django.test import TestCase
+from billing import get_gateway, CreditCard
+from billing.gateway import CardNotSupported
+from billing.utils.credit_card import Visa
+
+
+class SamuraiTestCase(TestCase):
+ def setUp(self):
+ self.merchant = get_gateway("samurai")
+ self.credit_card = CreditCard(first_name="Test", last_name="User",
+ month=10, year=2012,
+ number="4111111111111111",
+ verification_value="100")
+
+ def testCardSupported(self):
+ self.credit_card.number = "5019222222222222"
+ self.assertRaises(CardNotSupported,
+ lambda: self.merchant.purchase(1000, self.credit_card))
+
+ def testCardType(self):
+ self.credit_card.number = '4242424242424242'
+ self.merchant.validate_card(self.credit_card)
+ self.assertEquals(self.credit_card.card_type, Visa)
+
+ def testPurchase(self):
+ resp = self.merchant.purchase(1, self.credit_card)
+ self.assertEquals(resp["status"], "SUCCESS")
+
+ def testStoreMissingCustomer(self):
+ self.assertRaises(TypeError,
+ lambda: self.merchant.store())
+
+ def testCredit(self):
+ resp = self.merchant.purchase(1000, self.credit_card)
+ self.assertEquals(resp["status"], "SUCCESS")
+ identification = resp["response"].reference_id
+ resp = self.merchant.credit(money=100, identification=identification)
+ self.assertEquals(resp["status"], "SUCCESS")
+
+ def testAuthorizeAndCapture(self):
+ resp = self.merchant.authorize(1000, self.credit_card)
+ self.assertEquals(resp["status"], "SUCCESS")
+ response = self.merchant.capture(50, resp["response"].reference_id)
+ self.assertEquals(response["status"], "SUCCESS")
+
+ def testStoreWithoutBillingAddress(self):
+ resp = self.merchant.store(self.credit_card)
+ self.assertEquals(resp["status"], "SUCCESS")
+ self.assertEquals(resp["response"].expiry_month, self.credit_card.month)
+ self.assertEquals(resp["response"].expiry_year, self.credit_card.year)
+ self.assertTrue(getattr(resp["response"], "payment_method_token"))
+ self.assertTrue(getattr(resp["response"], "created_at"))
+
+ def testUnstore(self):
+ resp = self.merchant.store(self.credit_card)
+ self.assertEquals(resp["status"], "SUCCESS")
+ response = self.merchant.unstore(resp["response"].payment_method_token)
+ self.assertEquals(response["status"], "SUCCESS")
View
78 billing/tests/stripe_tests.py
@@ -0,0 +1,78 @@
+from django.test import TestCase
+from billing import get_gateway, CreditCard
+from billing.gateway import CardNotSupported
+from billing.utils.credit_card import Visa
+import stripe
+from django.conf import settings
+
+
+class StripeGatewayTestCase(TestCase):
+ def setUp(self):
+ self.merchant = get_gateway("stripe")
+ self.credit_card = CreditCard(first_name="Test", last_name="User",
+ month=10, year=2012,
+ number="4242424242424242",
+ verification_value="100")
+ stripe.api_key = settings.STRIPE_API_KEY
+ self.stripe = stripe
+
+ def testCardSupported(self):
+ self.credit_card.number = "5019222222222222"
+ self.assertRaises(CardNotSupported,
+ lambda: self.merchant.purchase(1000, self.credit_card))
+
+ def testCardType(self):
+ self.credit_card.number = '4242424242424242'
+ self.merchant.validate_card(self.credit_card)
+ self.assertEquals(self.credit_card.card_type, Visa)
+
+ def testPurchase(self):
+ resp = self.merchant.purchase(1, self.credit_card)
+ self.assertEquals(resp["status"], "SUCCESS")
+
+ def testStoreMissingCustomer(self):
+ self.assertRaises(TypeError,
+ lambda: self.merchant.store())
+
+ def testStoreWithoutBillingAddress(self):
+ resp = self.merchant.store(self.credit_card)
+ self.assertEquals(resp["status"], "SUCCESS")
+ self.assertEquals(resp["response"].active_card.exp_month, self.credit_card.month)
+ self.assertEquals(resp["response"].active_card.exp_year, self.credit_card.year)
+ self.assertTrue(getattr(resp["response"], "id"))
+ self.assertTrue(getattr(resp["response"], "created"))
+
+ def testUnstore(self):
+ resp = self.merchant.store(self.credit_card)
+ self.assertEquals(resp["status"], "SUCCESS")
+ response = self.merchant.unstore(resp["response"].id)
+ self.assertEquals(response["status"], "SUCCESS")
+
+ def testRecurring1(self):
+ try:
+ plan = self.stripe.Plan.retrieve("plaba")
+ except self.stripe.InvalidRequestError:
+ response = self.stripe.Plan.create(
+ amount=1000,
+ interval='month',
+ name="plaban",
+ currency="usd",
+ id="plaba")
+ options = {"plan_id": "plaba"}
+ resp = self.merchant.recurring(self.credit_card, options=options)
+ self.assertEquals(resp["status"], "SUCCESS")
+ subscription = resp["response"].subscription
+ self.assertEquals(subscription.status, "active")
+
+ def testCredit(self):
+ resp = self.merchant.purchase(1, self.credit_card)
+ self.assertEquals(resp["status"], "SUCCESS")
+ identification = resp["response"].id
+ resp = self.merchant.credit(identification=identification)
+ self.assertEquals(resp["status"], "SUCCESS")
+
+ def testAuthorizeAndCapture(self):
+ resp = self.merchant.authorize(100, self.credit_card)
+ self.assertEquals(resp["status"], "SUCCESS")
+ response = self.merchant.capture(50, resp["response"].id)
+ self.assertEquals(response["status"], "SUCCESS")
View
68 docs/gateways/samurai_gateway.rst
@@ -0,0 +1,68 @@
+-----------------------------------
+Samurai Payments
+-----------------------------------
+
+`Samurai Payments`_ is a gateway provided by `FeeFighters`_
+to services which are willing to take the burden of PCI compliance. This does not involve
+any redirects and only Server to Server calls happen in the background.
+
+.. note::
+
+ You will require the official `samurai`_ python package offered by Samurai
+ for this gateway to work.
+
+Settings attributes required for this integration are:
+
+* ``SAMURAI_MERCHANT_KEY``: The merchant key provided by Samurai.
+ Can be obtained from the account dashboard.
+* ``SAMURAI_MERCHANT_PASSWORD``: The merchant password provided by Samurai through their account
+ dashboard.
+* ``SAMURAI_PROCESSOR_TOKEN``: The processor token provided by Samurai through their account
+ dashboard.
+
+Example:
+---------
+
+ Simple usage::
+
+ >>> samurai = get_gateway("samurai")
+ >>> credit_card = CreditCard(first_name="Test", last_name="User",
+ month=10, year=2012,
+ number="4111111111111111",
+ verification_value="100")
+
+ # Bill the user for 1000 USD
+ >>> resp = samurai.purchase(1000, credit_card)
+ >>> resp["response"].is_success()
+ True
+
+ # Authorize the card for 1000 USD
+ >>> resp = samurai.authorize(1000, credit_card)
+
+ # Capture funds (900 USD) from a previously authorized transaction
+ >>> response = samurai.capture(900, resp["response"].reference_id)
+ >>> response["response"].is_success()
+ True
+
+ # Void an authorized transaction
+ >>> samurai.void(resp["response"].reference_id)
+
+ # Store Customer and Credit Card information in the vault
+ >>> resp = samurai.store(credit_card)
+
+ # Unstore a previously stored credit card from the vault
+ >>> response = samurai.unstore(resp["response"].payment_method_token)
+ >>> response["response"].is_redacted
+ True
+
+ # Credit the amount back after a purchase
+ >>> response = samurai.unstore(1000,credit_card)
+ >>> response = samurai.credit(100,response["response"].reference_id)
+ >>> response["response"].is_success()
+ True
+
+
+
+.. _`Samurai Payments`: https://samurai.feefighters.com
+.. _`FeeFighters`: http://feefighters.com/
+.. _`samurai`: http://pypi.python.org/pypi/samurai/0.6
View
63 docs/gateways/stripe_payment.rst
@@ -0,0 +1,63 @@
+-----------------------------------
+Stripe Payments
+-----------------------------------
+
+`Stripe Payments`_ is a gateway provided by `Stripe`_
+to services which are willing to take the burden of PCI compliance. This does not involve
+any redirects and only Server to Server calls happen in the background.
+
+.. note::
+
+ You will require the official `stripe`_ python package offered by Stripe
+ for this gateway to work.
+
+Settings attributes required for this integration are:
+
+* ``STRIPE_API_KEY``: The merchant api key is provided by Stripe.
+ Can be obtained from the account dashboard.
+
+
+Example:
+---------
+
+ Simple usage::
+
+ >>> stripe = get_gateway("stripe")
+ >>> credit_card = CreditCard(first_name="Test", last_name="User",
+ month=10, year=2012,
+ number="4242424242424242",
+ verification_value="100")
+
+ # Bill the user for 1000 USD
+ >>> resp = stripe.purchase(1000, credit_card)
+ >>> resp["status"]
+ SUCCESS
+
+ # Authorize the card for 1000 USD
+ >>> resp = stripe.authorize(1000, credit_card)
+
+ # Capture funds (900 USD) from a previously authorized transaction
+ >>> response = stripe.capture(900, resp["response"].id)
+ >>> response["status"]
+ SUCCESS
+
+
+ # Store Customer and Credit Card information in the vault
+ >>> resp = stripe.store(credit_card)
+
+ # Unstore a previously stored credit card from the vault
+ >>> response = stripe.unstore(resp["response"].id)
+ >>> response["status"]
+ SUCCESS
+
+ # A recurring plan charge
+ >>> options = {"plan_id": "gold"}
+ >>> resp = stripe.recurring(credit_card, options = options)
+ >>> resp["status"]
+ SUCCESS
+
+
+
+.. _`Stripe Payments Docs`: https://stripe.com/docs
+.. _`Stripe Payments`: https://stripe.com/
+.. _`stripe`: http://pypi.python.org/pypi/stripe/
View
4 docs/index.rst
@@ -22,6 +22,8 @@ Contents:
* :doc:`Authorize.Net <gateways/authorize_net>`
* :doc:`eWay <gateways/eway_gateway>`
* :doc:`Braintree Payments Server to Server <gateways/braintree_payments>`
+ * :doc:`Stripe Payments <gateways/stripe_payment>`
+ * :doc:`Samurai Payment <gateways/samurai_gateway>`
* :doc:`Off-site Processing <offsite_processing>`
@@ -30,6 +32,8 @@ Contents:
* :doc:`RBS WorldPay <offsite/rbs_worldpay>`
* :doc:`Amazon FPS <offsite/amazon_fps>`
* :doc:`Braintree Payments Transparent Redirect <offsite/braintree_payments>`
+ * :doc:`Stripe <offsite/stripe_integration>`
+ * :doc:`Samurai <offsite/samurai_integration>`
* :doc:`Signals <signals>`
* :doc:`Writing your own gateway <custom_gateway>`
View
68 docs/offsite/samurai_integration.rst
@@ -0,0 +1,68 @@
+----------------------------------------
+Samurai Payment Integration
+----------------------------------------
+
+`Samurai Payment Integration`_ is a service offered by
+`FeeFighters`_ to reduce the complexity of PCI compliance.
+
+.. note::
+
+ This integration makes use of the official `samurai`_ python package offered
+ by Samurai Payments. Please install it before you use this integration.
+
+Settings attributes required for this integration are:
+
+
+
+Here are the methods and attributes implemented on the ``SamuraiIntegration`` class:
+
+* ``__init__(self, options=None)``: The constructor method
+
+* ``get_urls(self)``: The method sets the url to which the token is sent
+ after the it is obtained from Samurai. This method is generally mapped
+ directly in the ``urls.py``.
+
+ .. code::
+
+ from billing import get_integration
+
+ samurai = get_integration("samurai")
+
+ urlpatterns += patterns('',
+ (r'^samurai/', include(samurai_obj.urls)),
+ )
+
+* ``get_token(self, request)``: The view method that recieves the
+token
+
+* ``generate_form(self)``: The method that generates and returns the form (present in
+ ``billing.forms.samurai_forms``)
+
+
+Example:
+--------
+
+ In the views.py::
+
+ samurai_obj = get_integration("samurai")
+ return render_to_response("some_template.html",
+ {"samurai_obj": samurai_obj},
+ context_instance=RequestContext(request))
+
+ In the urls.py::
+
+ samurai_obj = get_integration("samurai")
+ urlpatterns += patterns('',
+ (r'^samurai/', include(samurai_obj.urls)),
+ )
+
+ In the template::
+
+ {% load billing_tags %}
+
+ {% samurai_payment samurai_obj %}
+
+
+.. _`Samurai Payment`: https://samurai.feefighters.com/
+.. _`samurai`: http://pypi.python.org/pypi/samurai/0.6
+.. _`FeeFighters`: http://feefighters.com/
View
69 docs/offsite/stripe_integration.rst
@@ -0,0 +1,69 @@
+----------------------------------------
+Stripe Payment Integration
+----------------------------------------
+
+`Stripe Payment Integration`_ is a service offered by
+`Stripe Payment`_ to reduce the complexity of PCI compliance.
+
+.. note::
+
+ This integration makes use of the official `stripe`_ python package offered
+ by Stripe Payments. Please install it before you use this integration.
+
+Settings attributes required for this integration are:
+
+
+
+Here are the methods and attributes implemented on the ``StripeIntegration`` class:
+
+* ``__init__(self, options=None)``: The constructor method that configures the
+ stripe setting
+
+* ``get_urls(self)``: The method sets the url to which the token is sent
+ after the it is obtained from Stripe. This method is generally mapped
+ directly in the ``urls.py``.
+
+ .. code::
+
+ from billing import get_integration
+
+ stripe = get_integration("stripe")
+
+ urlpatterns += patterns('',
+ (r'^stripe/', include(stripe_obj.urls)),
+ )
+
+* ``get_token(self, request)``: The view method that recieves the
+token
+
+* ``generate_form(self)``: The method that generates and returns the form (present in
+ ``billing.forms.stripe_form``)
+
+
+Example:
+--------
+
+ In the views.py::
+
+ stripe_obj = get_integration("stripe")
+ return render_to_response("some_template.html",
+ {"stripe_obj": stripe_obj},
+ context_instance=RequestContext(request))
+
+ In the urls.py::
+
+ stripe_obj = get_integration("stripe")
+ urlpatterns += patterns('',
+ (r'^stripe/', include(stripe_obj.urls)),
+ )
+
+ In the template::
+
+ {% load billing_tags %}
+
+ {% stripe_payment stripe_obj %}
+
+
+.. _`Stripe Payment`: https://stripe.com
+.. _`stripe`: http://pypi.python.org/pypi/stripe/
+
View
6 example/app/forms.py
@@ -5,6 +5,7 @@
from billing import CreditCard
+
class CreditCardForm(forms.Form):
CARD_TYPES = [
('', ''),
@@ -14,7 +15,7 @@ class CreditCardForm(forms.Form):
('american_express', 'American Express'),
('diners_club', 'Diners Club'),
# ('jcb', ''),
- # ('switch', ''),
+ # ('switch', ''),
# ('solo', ''),
# ('dankort', ''),
('maestro', 'Maestro'),
@@ -25,7 +26,6 @@ class CreditCardForm(forms.Form):
today = datetime.date.today()
MONTH_CHOICES = [(m, datetime.date(today.year, m, 1).strftime('%b')) for m in range(1, 13)]
YEAR_CHOICES = [(y, y) for y in range(today.year, today.year + 21)]
-
first_name = forms.CharField()
last_name = forms.CharField()
month = forms.ChoiceField(choices=MONTH_CHOICES)
@@ -40,5 +40,3 @@ def clean(self):
if not credit_card.is_valid():
raise forms.ValidationError('Credit card validation failed')
return data
-
-
View
6 example/app/templates/app/base.html
@@ -1,3 +1,4 @@
+
<html>
<head>
@@ -10,7 +11,9 @@
<a href='{% url app_authorize %}'>Authorize</a>
<a href='{% url app_eway %}'>Eway</a>
<a href='{% url app_paypal %}'>Paypal</a>
- <a href='{% url app_braintree %}'>Braintree Payments (S2S)</a>
+ <a href='{% url app_braintree %}'>Braintree Payments (S2S)</a>
+ <a href='{% url app_stripe %}'>Stripe Payment</a>
+ <a href='{% url app_samurai %}'>Samurai</a>
<br/><br/>
Offsite:
<a href='{% url app_offsite_paypal %}'>Paypal</a>
@@ -18,6 +21,7 @@
<a href='{% url app_offsite_world_pay %}'>WorldPay</a>
<a href='{% url app_offsite_amazon_fps %}'>Amazon FPS</a>
<a href='{% url app_offsite_braintree %}'>Braintree Payments (TR)</a>
+ <a href='{% url app_offsite_stripe %}'> Stripe </a>
<h1>{{ title }} </h1>
{% block content %}
View
50 example/app/templates/app/samurai.html
@@ -0,0 +1,50 @@
+{% extends "app/base.html" %}
+{% load billing_tags %}
+
+{% block content %}
+<script type="text/javascript" src="http://code.jquery.com/jquery-1.7.1.js"></script>
+<script type="text/javascript" src="https://samurai.feefighters.com/assets/api/samurai.js"></script>
+<script type="text/javascript">
+Samurai.init({
+ merchant_key: '97ab1f01469c4170a7e3a92e',
+ sandbox: true
+});
+</script>
+<script type="text/javascript">
+
+
+$(document).ready(function() {
+
+
+ $("#payment-form").submit(function(event) {
+
+ $('.submit-button').attr("disabled", "disabled");
+
+Samurai.payment({
+ credit_card: {
+
+ card_number: $('#id_credit_card__number').val(),
+ cvv: $('#id_credit_card__cvc').val(),
+ expiry_month: $('#id_credit_card__expiration_month').val(),
+ expiry_year: $('#id_credit_card__expiration_year').val()
+ }
+}, function(data) {
+
+ var url = "/samurai/samurai-get-token/";
+ var token = data.payment_method.payment_method_token;
+ $.post(url, {
+ samuraiToken:token
+});
+});
+
+
+
+ return false;
+ });
+
+});
+</script>
+
+
+{% samurai_payment samurai_obj %}
+{% endblock %}
View
54 example/app/templates/app/stripe.html
@@ -0,0 +1,54 @@
+{% extends "app/base.html" %}
+{% load billing_tags %}
+
+{% block content %}
+<script type="text/javascript" src="http://code.jquery.com/jquery-1.7.1.js"></script>
+<script type="text/javascript" src="https://js.stripe.com/v1/"></script>
+
+<script type="text/javascript">
+
+
+Stripe.setPublishableKey('pk_K2Z3rk1gTlIjSPRwDNyw3KEuDWmXY');
+
+$(document).ready(function() {
+ function stripeResponseHandler(status, response) {
+
+console.log(response);
+ if (response.error) {
+
+ //show the errors on the form
+ $(".payment-errors").html(response.error.message);
+
+ } else {
+
+ var url = "/stripe/stripe-get-token/";
+ var token = response['id'];
+ $.post(url, {
+ stripeToken:token
+ });
+ }
+ }
+
+ $("#payment-form").submit(function(event) {
+
+ // disable the submit button to prevent repeated clicks
+ $('.submit-button').attr("disabled", "disabled");
+
+ var amount = 1000; //amount you want to charge in cents
+ Stripe.createToken({
+ number: $('#id_credit_card__number').val(),
+ cvc: $('#id_credit_card__cvc').val(),
+ exp_month: $('#id_credit_card__expiration_month').val(),
+ exp_year: $('#id_credit_card__expiration_year').val()
+ }, amount, stripeResponseHandler);
+
+
+
+ return false;
+ });
+
+});
+</script>
+
+{% stripe_payment stripe_obj %}
+{% endblock %}
View
20 example/app/urls.py
@@ -8,6 +8,8 @@
fps_recur_obj = get_integration("fps")
world_pay_obj = get_integration("world_pay")
braintree_obj = get_integration("braintree_payments")
+stripe_obj = get_integration("stripe")
+samurai_obj = get_integration("samurai")
urlpatterns = patterns('app.views',
url(r'^$', 'index', name='app_index'),
@@ -15,6 +17,8 @@
url(r'^paypal/$', 'paypal', name='app_paypal'),
url(r'^eway/$', 'eway', name='app_eway'),
url(r'^braintree/$', 'braintree', name='app_braintree'),
+ url(r'^stripe/$', 'stripe', name='app_stripe'),
+ url(r'^samurai/$', 'samurai', name='app_samurai'),
)
# offsite payments
@@ -24,6 +28,8 @@
url(r'offsite/world_pay/$', 'offsite_world_pay', name='app_offsite_world_pay'),
url(r'offsite/amazon_fps/$', 'offsite_amazon_fps', name='app_offsite_amazon_fps'),
url(r'offsite/braintree/$', 'offsite_braintree', name='app_offsite_braintree'),
+ url(r'offsite/stripe/$', 'offsite_stripe', name='app_offsite_stripe'),
+ url(r'offsite/samurai/$', 'offsite_samurai', name='app_offsite_samurai'),
)
# paypal payment notification handler
@@ -41,14 +47,20 @@
urlpatterns += patterns('',
(r'^braintree/', include(braintree_obj.urls)),
)
+urlpatterns += patterns('',
+ (r'^stripe/', include(stripe_obj.urls)),
+)
+urlpatterns += patterns('',
+ (r'^samurai/', include(samurai_obj.urls)),
+)
urlpatterns += patterns('django.views.generic.simple',
- url(r'offsite/paypal/done/$',
- 'direct_to_template',
+ url(r'offsite/paypal/done/$',
+ 'direct_to_template',
{'template': 'app/payment_done.html'},
name='app_offsite_paypal_done'),
- url(r'offsite/google-checkout/done/$',
- 'direct_to_template',
+ url(r'offsite/google-checkout/done/$',
+ 'direct_to_template',
{'template': 'app/payment_done.html'},
name='app_offsite_google_checkout_done'),
)
View
54 example/app/views.py
@@ -11,7 +11,8 @@
from app.forms import CreditCardForm
from app.urls import (google_checkout_obj, world_pay_obj,
pay_pal_obj, amazon_fps_obj,
- fps_recur_obj, braintree_obj)
+ fps_recur_obj, braintree_obj,
+ stripe_obj,samurai_obj)
from django.conf import settings
from django.contrib.sites.models import RequestSite
@@ -37,8 +38,8 @@ def authorize(request):
response = merchant.purchase(amount, credit_card)
#response = merchant.recurring(amount, credit_card)
else:
- form = CreditCardForm(initial={'number':'4222222222222'})
- return render(request, 'app/index.html', {'form': form,
+ form = CreditCardForm(initial={'number': '4222222222222'})
+ return render(request, 'app/index.html', {'form': form,
'amount': amount,
'response': response,
'title': 'Authorize'})
@@ -60,7 +61,7 @@ def paypal(request):
# response = merchant.purchase(amount, credit_card, options={'request': request})
response = merchant.recurring(amount, credit_card, options={'request': request})
else:
- form = CreditCardForm(initial={'number':'4797503429879309',
+ form = CreditCardForm(initial={'number': '4797503429879309',
'verification_value': '037',
'month': 1,
'year': 2019,
@@ -132,7 +133,40 @@ def braintree(request):
'amount': amount,
'response': response,
'title': 'Braintree Payments (S2S)'})
+def stripe(request):
+ amount = 1
+ response= None
+ if request.method == 'POST':
+ form = CreditCardForm(request.POST)
+ if form.is_valid():
+ data = form.cleaned_data
+ credit_card = CreditCard(**data)
+ merchant = get_gateway("stripe")
+ response = merchant.purchase(amount,credit_card)
+ else:
+ form = CreditCardForm(initial={'number':'4242424242424242'})
+ return render(request, 'app/index.html',{'form': form,
+ 'amount':amount,
+ 'response':response,
+ 'title':'Stripe Payment'})
+def samurai(request):
+ amount = 1
+ response= None
+ if request.method == 'POST':
+ form = CreditCardForm(request.POST)
+ if form.is_valid():
+ data = form.cleaned_data
+ credit_card = CreditCard(**data)
+ merchant = get_gateway("samurai")
+ response = merchant.purchase(amount,credit_card)
+ else:
+ form = CreditCardForm(initial={'number':'4111111111111111'})
+ return render(request, 'app/index.html',{'form': form,
+ 'amount':amount,
+ 'response':response,
+ 'title':'Samurai'})
+
def offsite_paypal(request):
@@ -215,3 +249,15 @@ def offsite_braintree(request):
template_vars = {'title': 'Braintree Payments Transparent Redirect',
"bp_obj": braintree_obj}
return render(request, "app/braintree_tr.html", template_vars)
+
+def offsite_stripe(request):
+
+ template_vars = {'title': 'Stripe Non PCI Compliance',
+ "stripe_obj": stripe_obj}
+ return render(request, "app/stripe.html", template_vars)
+
+def offsite_samurai(request):
+ template_vars = {'title': 'Samurai Non PCI Compliance',
+ "samurai_obj": samurai_obj}
+ return render(request, "app/samurai.html", template_vars)
+
View
7 example/localsettings.example.py
@@ -43,6 +43,13 @@
BRAINTREE_PUBLIC_KEY = ""
BRAINTREE_PRIVATE_KEY = ""
+#Stripe Payment Settings
+STRIPE_API_KEY = ''
+
+#SAMURAI Settings
+SAMURAI_MERCHANT_KEY = ''
+SAMURAI_MERCHANT_PASSWORD = ''
+SAMURAI_PROCESSOR_TOKEN = ''
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
View
2  example/settings.py
@@ -99,6 +99,8 @@
'paypal.standard',
'paypal.pro',
'paypal.standard.ipn',
+ 'stripe',
+ 'samurai'
)
STATIC_URL = "http://merchant.agiliq.com/"
Something went wrong with that request. Please try again.