2012-08-30 23:51:59 +00:00
# https://github.com/stripe/stripe-python
# https://stripe.com/docs/api?lang=python#top
2013-06-03 16:31:39 +00:00
"""
external library imports
"""
2012-09-24 23:29:20 +00:00
import logging
2012-10-22 14:50:52 +00:00
import json
2013-06-03 16:31:39 +00:00
2012-09-24 23:29:20 +00:00
from datetime import datetime , timedelta
2012-10-18 13:59:08 +00:00
from itertools import islice
2013-06-03 16:31:39 +00:00
from pytz import utc
2018-04-20 01:26:29 +00:00
import re
import unittest
from unittest import TestCase
import stripe
2012-09-01 05:59:22 +00:00
2013-06-03 16:31:39 +00:00
"""
django imports
"""
2012-09-24 23:29:20 +00:00
from django . conf import settings
2012-10-23 21:20:13 +00:00
from django . core . mail import send_mail
2013-06-03 16:31:39 +00:00
from django . http import HttpResponse
2018-04-19 16:24:34 +00:00
from django . utils . timezone import now
2012-09-24 23:29:20 +00:00
2013-06-03 16:31:39 +00:00
"""
regluit imports
"""
[#37053797] reverting stripelib.py -- Sn tests run but django command test_stripe_charge fails: done so Eric can fix circular import issue:
>>> >>> >>> (regluitdj14)raymond-yees-computer:regluit raymondyee$ django-admin.py test_stripe_charge
Traceback (most recent call last):
File "/Users/raymondyee/.virtualenvs/regluitdj14/bin/django-admin.py", line 5, in <module>
management.execute_from_command_line()
File "/Users/raymondyee/.virtualenvs/regluitdj14/lib/python2.7/site-packages/django/core/management/__init__.py", line 443, in execute_from_command_line
utility.execute()
File "/Users/raymondyee/.virtualenvs/regluitdj14/lib/python2.7/site-packages/django/core/management/__init__.py", line 382, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "/Users/raymondyee/.virtualenvs/regluitdj14/lib/python2.7/site-packages/django/core/management/__init__.py", line 261, in fetch_command
klass = load_command_class(app_name, subcommand)
File "/Users/raymondyee/.virtualenvs/regluitdj14/lib/python2.7/site-packages/django/core/management/__init__.py", line 69, in load_command_class
module = import_module('%s.management.commands.%s' % (app_name, name))
File "/Users/raymondyee/.virtualenvs/regluitdj14/lib/python2.7/site-packages/django/utils/importlib.py", line 35, in import_module
__import__(name)
File "/Users/raymondyee/C/src/Gluejar/regluit/payment/management/commands/test_stripe_charge.py", line 2, in <module>
from regluit.payment import stripelib
File "/Users/raymondyee/C/src/Gluejar/regluit/payment/stripelib.py", line 10, in <module>
from regluit.payment.models import Account
File "/Users/raymondyee/C/src/Gluejar/regluit/payment/models.py", line 4, in <module>
from regluit.core.models import Campaign, Wishlist, Premium, PledgeExtra
File "/Users/raymondyee/C/src/Gluejar/regluit/core/models.py", line 998, in <module>
from regluit.payment.manager import PaymentManager
File "/Users/raymondyee/C/src/Gluejar/regluit/payment/manager.py", line 2, in <module>
from regluit.payment.models import Transaction, Receiver, PaymentResponse
ImportError: cannot import name Transaction
2012-10-02 19:02:45 +00:00
from regluit . payment import baseprocessor
2013-06-03 16:31:39 +00:00
from regluit . payment . models import Account , Transaction , PaymentResponse
from regluit . payment . parameters import (
PAYMENT_HOST_STRIPE ,
TRANSACTION_STATUS_ACTIVE ,
TRANSACTION_STATUS_COMPLETE ,
TRANSACTION_STATUS_ERROR ,
PAYMENT_TYPE_AUTHORIZATION ,
2013-06-28 23:05:19 +00:00
PAYMENT_TYPE_INSTANT ,
2013-06-03 16:31:39 +00:00
TRANSACTION_STATUS_CANCELED
)
2012-11-06 19:22:25 +00:00
from regluit . payment . signals import transaction_charged , transaction_failed
2012-09-21 18:24:42 +00:00
2013-07-15 21:52:29 +00:00
# as of 2013.07.15
# ['charge.disputed', 'coupon.updated'] are legacy events -- don't know whether to
# include them in list
STRIPE_EVENT_TYPES = [ ' account.updated ' , ' account.application.deauthorized ' , ' balance.available ' ,
' charge.succeeded ' , ' charge.failed ' , ' charge.refunded ' , ' charge.captured ' ,
' charge.dispute.created ' , ' charge.dispute.updated ' , ' charge.dispute.closed ' ,
' customer.created ' , ' customer.updated ' , ' customer.deleted ' ,
' customer.card.created ' , ' customer.card.updated ' , ' customer.card.deleted ' ,
2018-01-08 01:21:48 +00:00
' customer.source.created ' , ' customer.source.deleted ' , ' customer.source.expiring ' ,
' customer.source.updated ' ,
2013-07-15 21:52:29 +00:00
' customer.subscription.created ' , ' customer.subscription.updated ' ,
' customer.subscription.deleted ' , ' customer.subscription.trial_will_end ' ,
' customer.discount.created ' , ' customer.discount.updated ' ,
' customer.discount.deleted ' , ' invoice.created ' , ' invoice.updated ' ,
' invoice.payment_succeeded ' , ' invoice.payment_failed ' , ' invoiceitem.created ' ,
' invoiceitem.updated ' , ' invoiceitem.deleted ' , ' plan.created ' , ' plan.updated ' ,
' plan.deleted ' , ' coupon.created ' , ' coupon.deleted ' , ' transfer.created ' ,
' transfer.updated ' , ' transfer.paid ' , ' transfer.failed ' , ' ping ' ]
2012-11-05 18:44:24 +00:00
2012-09-24 23:29:20 +00:00
logger = logging . getLogger ( __name__ )
2017-07-27 14:33:13 +00:00
# https://stackoverflow.com/questions/2348317/how-to-write-a-pager-for-python-iterators/2350904#2350904
2012-10-18 13:59:08 +00:00
def grouper ( iterable , page_size ) :
page = [ ]
for item in iterable :
page . append ( item )
if len ( page ) == page_size :
yield page
page = [ ]
if len ( page ) :
yield page
2012-10-06 16:51:43 +00:00
class StripelibError ( baseprocessor . ProcessorError ) :
2012-09-24 23:29:20 +00:00
pass
2012-08-30 23:51:59 +00:00
2012-08-30 16:24:42 +00:00
# if customer.id doesn't exist, create one and then charge the customer
# we probably should ask our users whether they are ok with our creating a customer id account -- or ask for credit
# card info each time....
2012-08-17 00:24:18 +00:00
# should load the keys for Stripe from db -- but for now just hardcode here
2012-09-05 23:11:32 +00:00
# moving towards not having the stripe api key for the non profit partner in the unglue.it code -- but in a logically
# distinct application
2012-10-15 13:24:12 +00:00
TEST_STRIPE_PK = ' pk_0EajXPn195ZdF7Gt7pCxsqRhNN5BF '
TEST_STRIPE_SK = ' sk_0EajIO4Dnh646KPIgLWGcO10f9qnH '
2012-08-17 00:24:18 +00:00
try :
from regluit . core . models import Key
STRIPE_PK = Key . objects . get ( name = " STRIPE_PK " ) . value
STRIPE_SK = Key . objects . get ( name = " STRIPE_SK " ) . value
logger . info ( ' Successful loading of STRIPE_*_KEYs ' )
except Exception , e :
2012-08-30 23:51:59 +00:00
# currently test keys for Gluejar and for raymond.yee@gmail.com as standin for non-profit
2012-10-13 23:42:24 +00:00
logger . info ( ' Exception {0} Need to use TEST STRIPE_*_KEYs ' . format ( e ) )
2012-10-18 13:59:08 +00:00
STRIPE_PK = TEST_STRIPE_PK
STRIPE_SK = TEST_STRIPE_SK
2012-08-17 00:24:18 +00:00
2012-08-30 23:51:59 +00:00
# set default stripe api_key to that of unglue.it
2013-06-21 22:14:51 +00:00
stripe . api_key = STRIPE_SK
# maybe we should be able to set this in django.settings...
# to start with, let's try hard-coding the api_version
# https://stripe.com/docs/upgrades?since=2012-07-09#api-changelog
#API_VERSION = '2012-07-09'
API_VERSION = ' 2013-02-13 '
stripe . api_version = API_VERSION
2012-08-30 23:51:59 +00:00
2012-08-30 16:24:42 +00:00
# https://stripe.com/docs/testing
TEST_CARDS = (
( ' 4242424242424242 ' , ' Visa ' ) ,
( ' 4012888888881881 ' , ' Visa ' ) ,
( ' 5555555555554444 ' , ' MasterCard ' ) ,
( ' 5105105105105100 ' , ' MasterCard ' ) ,
( ' 378282246310005 ' , ' American Express ' ) ,
( ' 371449635398431 ' , ' American Express ' ) ,
( ' 6011111111111117 ' , ' Discover ' ) ,
( ' 6011000990139424 ' , ' Discover ' ) ,
( ' 30569309025904 ' , " Diner ' s Club " ) ,
( ' 38520000023237 ' , " Diner ' s Club " ) ,
( ' 3530111333300000 ' , ' JCB ' ) ,
( ' 3566002020360505 ' , ' JCB ' )
)
2012-08-31 18:19:05 +00:00
ERROR_TESTING = dict ( (
( ' ADDRESS1_ZIP_FAIL ' , ( ' 4000000000000010 ' , ' address_line1_check and address_zip_check will both fail ' ) ) ,
( ' ADDRESS1_FAIL ' , ( ' 4000000000000028 ' , ' address_line1_check will fail. ' ) ) ,
( ' ADDRESS_ZIP_FAIL ' , ( ' 4000000000000036 ' , ' address_zip_check will fail. ' ) ) ,
( ' CVC_CHECK_FAIL ' , ( ' 4000000000000101 ' , ' cvc_check will fail. ' ) ) ,
2012-09-01 05:59:22 +00:00
( ' BAD_ATTACHED_CARD ' , ( ' 4000000000000341 ' , ' Attaching this card to a Customer object will succeed, but attempts to charge the customer will fail. ' ) ) ,
2012-08-31 18:19:05 +00:00
( ' CHARGE_DECLINE ' , ( ' 4000000000000002 ' , ' Charges with this card will always be declined. ' ) )
) )
2012-08-30 16:24:42 +00:00
2012-10-06 16:51:43 +00:00
CARD_FIELDS_TO_COMPARE = ( ' exp_month ' , ' exp_year ' , ' name ' , ' address_line1 ' , ' address_line2 ' , ' address_zip ' , ' address_state ' )
2012-09-01 05:59:22 +00:00
# types of errors / when they can be handled
2012-08-30 16:24:42 +00:00
#card_declined: Use this special card number - 4000000000000002.
#incorrect_number: Use a number that fails the Luhn check, e.g. 4242424242424241.
#invalid_expiry_month: Use an invalid month e.g. 13.
#invalid_expiry_year: Use a year in the past e.g. 1970.
#invalid_cvc: Use a two digit number e.g. 99.
def filter_none ( d ) :
return dict ( [ ( k , v ) for ( k , v ) in d . items ( ) if v is not None ] )
2012-08-17 00:24:18 +00:00
# if you create a Customer object, then you'll be able to charge multiple times. You can create a customer with a token.
2017-07-27 14:33:13 +00:00
# https://en.wikipedia.org/wiki/Luhn_algorithm#Implementation_of_standard_Mod_10
2012-10-08 20:08:28 +00:00
def luhn_checksum ( card_number ) :
def digits_of ( n ) :
return [ int ( d ) for d in str ( n ) ]
digits = digits_of ( card_number )
odd_digits = digits [ - 1 : : - 2 ]
even_digits = digits [ - 2 : : - 2 ]
checksum = 0
checksum + = sum ( odd_digits )
for d in even_digits :
checksum + = sum ( digits_of ( d * 2 ) )
return checksum % 10
def is_luhn_valid ( card_number ) :
return luhn_checksum ( card_number ) == 0
2012-08-17 00:24:18 +00:00
# https://stripe.com/docs/tutorials/charges
2012-10-06 16:51:43 +00:00
def card ( number = TEST_CARDS [ 0 ] [ 0 ] , exp_month = 1 , exp_year = 2020 , cvc = None , name = None ,
2012-08-30 16:24:42 +00:00
address_line1 = None , address_line2 = None , address_zip = None , address_state = None , address_country = None ) :
2012-10-06 16:51:43 +00:00
""" Note: there is no place to enter address_city in the API """
2012-08-30 16:24:42 +00:00
card = {
" number " : number ,
2012-10-06 16:51:43 +00:00
" exp_month " : int ( exp_month ) ,
" exp_year " : int ( exp_year ) ,
" cvc " : int ( cvc ) if cvc is not None else None ,
2012-08-30 16:24:42 +00:00
" name " : name ,
" address_line1 " : address_line1 ,
" address_line2 " : address_line2 ,
" address_zip " : address_zip ,
" address_state " : address_state ,
" address_country " : address_country
}
return filter_none ( card )
2012-10-20 01:11:46 +00:00
def _isListableAPIResource ( x ) :
""" test whether x is an instance of the stripe.ListableAPIResource class """
try :
return issubclass ( x , stripe . ListableAPIResource )
except :
return False
2012-08-30 16:24:42 +00:00
2013-06-21 22:14:51 +00:00
2012-08-30 16:24:42 +00:00
class StripeClient ( object ) :
def __init__ ( self , api_key = STRIPE_SK ) :
self . api_key = api_key
2012-08-17 00:24:18 +00:00
2012-08-30 23:51:59 +00:00
# key entities: Charge, Customer, Token, Event
2012-08-30 16:24:42 +00:00
@property
def charge ( self ) :
return stripe . Charge ( api_key = self . api_key )
2012-08-17 00:24:18 +00:00
2012-08-30 16:24:42 +00:00
@property
def customer ( self ) :
return stripe . Customer ( api_key = self . api_key )
2012-08-17 00:24:18 +00:00
2012-08-30 16:24:42 +00:00
@property
def token ( self ) :
return stripe . Token ( api_key = self . api_key )
2012-09-01 05:59:22 +00:00
@property
def transfer ( self ) :
return stripe . Transfer ( api_key = self . api_key )
2012-08-17 00:24:18 +00:00
2012-08-30 16:24:42 +00:00
@property
def event ( self ) :
return stripe . Event ( api_key = self . api_key )
2012-09-01 05:59:22 +00:00
2012-08-30 16:24:42 +00:00
def create_token ( self , card ) :
return stripe . Token ( api_key = self . api_key ) . create ( card = card )
2012-08-17 00:24:18 +00:00
2012-10-08 20:08:28 +00:00
def create_customer ( self , card = None , description = None , email = None , account_balance = None , plan = None , trial_end = None ) :
2012-08-30 16:24:42 +00:00
""" card is a dictionary or a token """
# https://stripe.com/docs/api?lang=python#create_customer
2012-08-17 00:24:18 +00:00
2012-08-30 16:24:42 +00:00
customer = stripe . Customer ( api_key = self . api_key ) . create (
card = card ,
description = description ,
email = email ,
account_balance = account_balance ,
plan = plan ,
trial_end = trial_end
)
# customer.id is useful to save in db
return customer
def create_charge ( self , amount , currency = " usd " , customer = None , card = None , description = None ) :
# https://stripe.com/docs/api?lang=python#create_charge
2012-09-19 00:56:38 +00:00
# customer.id or card required but not both
2012-08-30 16:24:42 +00:00
# charge the Customer instead of the card
# amount in cents
2012-09-10 14:23:03 +00:00
2012-08-30 16:24:42 +00:00
charge = stripe . Charge ( api_key = self . api_key ) . create (
amount = int ( 100 * amount ) , # in cents
currency = currency ,
2012-09-19 00:56:38 +00:00
customer = customer ,
2012-09-10 14:23:03 +00:00
card = card ,
2012-08-30 16:24:42 +00:00
description = description
)
return charge
def refund_charge ( self , charge_id ) :
# https://stripe.com/docs/api?lang=python#refund_charge
ch = stripe . Charge ( api_key = self . api_key ) . retrieve ( charge_id )
ch . refund ( )
2012-10-20 01:11:46 +00:00
return ch
2012-10-18 13:59:08 +00:00
2012-10-20 01:11:46 +00:00
def _all_objs ( self , class_type , * * kwargs ) :
""" a generic iterator for all classes of type stripe.ListableAPIResource """
2012-10-18 13:59:08 +00:00
# type=None, created=None, count=None, offset=0
2012-10-20 01:11:46 +00:00
# obj_type: one of 'Charge','Coupon','Customer', 'Event','Invoice', 'InvoiceItem', 'Plan', 'Transfer'
try :
stripe_class = getattr ( stripe , class_type )
except :
yield StopIteration
else :
if _isListableAPIResource ( stripe_class ) :
kwargs2 = kwargs . copy ( )
kwargs2 . setdefault ( ' offset ' , 0 )
kwargs2 . setdefault ( ' count ' , 100 )
more_items = True
while more_items :
items = stripe_class ( api_key = self . api_key ) . all ( * * kwargs2 ) [ ' data ' ]
for item in items :
yield item
if len ( items ) :
kwargs2 [ ' offset ' ] + = len ( items )
else :
more_items = False
2012-10-18 13:59:08 +00:00
else :
2012-10-20 01:11:46 +00:00
yield StopIteration
def __getattribute__ ( self , name ) :
""" handle list_* calls """
mapping = { ' list_charges ' : " Charge " ,
' list_coupons ' : " Coupon " ,
' list_customers ' : " Customer " ,
' list_events ' : " Event " ,
' list_invoices ' : " Invoice " ,
' list_invoiceitems ' : " InvoiceItem " ,
' list_plans ' : " Plan " ,
' list_transfers ' : " Transfer "
}
if name in mapping . keys ( ) :
class_value = mapping [ name ]
def list_events ( * * kwargs ) :
for e in self . _all_objs ( class_value , * * kwargs ) :
yield e
return list_events
else :
return object . __getattribute__ ( self , name )
2012-10-18 13:59:08 +00:00
2012-09-01 05:59:22 +00:00
# can't test Transfer in test mode: "There are no transfers in test mode."
2012-08-30 23:51:59 +00:00
#pledge scenario
2012-08-31 18:19:05 +00:00
# bad card -- what types of erros to handle?
# https://stripe.com/docs/api#errors
2012-09-01 05:59:22 +00:00
2012-09-05 23:11:32 +00:00
# https://stripe.com/docs/api#event_types
# events of interest -- especially ones that do not directly arise immediately (synchronously) from something we do -- I think
# especially: charge.disputed
# I think following (charge.succeeded, charge.failed, charge.refunded) pretty much sychronous to our actions
2012-09-01 05:59:22 +00:00
# customer.created, customer.updated, customer.deleted
2012-09-05 23:11:32 +00:00
# transfer
# I expect the ones related to transfers all happen asynchronously: transfer.created, transfer.updated, transfer.failed
2012-09-01 05:59:22 +00:00
# When will the money I charge with Stripe end up in my bank account?
# Every day, we transfer the money that you charged seven days previously?that is, you receive the money for your March 1st charges on March 8th.
2012-08-31 18:19:05 +00:00
# pending payments?
# how to tell whether money transferred to bank account yet
# best practices for calling Events -- not too often.
2012-08-30 23:51:59 +00:00
2012-10-08 20:08:28 +00:00
# Errors we still need to catch:
#
# * invalid_number -- can't get stripe to generate for us. What it means:
#
# * that the card has been cancelled (or never existed to begin with
#
# * the card is technically correct (Luhn valid?)
#
# * the first 6 digits point to a valid bank
#
# * but the account number (the rest of the digits) doesn't correspond to a credit account with that bank
#
# * Brian of stripe.com suggests we could treat it the same way as we'd treat card_declined
#
# * processing_error:
#
# * means: something went wrong when stripe tried to make the charge (it could be that the card's issuing bank is down, or our connection to the bank isn't working properly)
# * we can retry -- e.g., a minute later, then 30 minutes, then an hour, 3 hours, a day.
# * we shouldn't see processing_error very often
#
# * expired_card -- also not easily simulatable in test mode
2012-10-20 01:11:46 +00:00
2012-10-06 16:51:43 +00:00
class StripeErrorTest ( TestCase ) :
""" Make sure the exceptions returned by stripe act as expected """
2012-10-08 20:08:28 +00:00
def test_cc_test_numbers_luhn_valid ( self ) :
""" Show that the test CC numbers supplied for testing as valid numbers are indeed Luhn valid """
self . assertTrue ( all ( [ is_luhn_valid ( c [ 0 ] ) for c in ERROR_TESTING . values ( ) ] ) )
2012-10-06 16:51:43 +00:00
def test_good_token ( self ) :
""" verify normal operation """
sc = StripeClient ( )
card1 = card ( number = TEST_CARDS [ 0 ] [ 0 ] , exp_month = 1 , exp_year = ' 2020 ' , cvc = ' 123 ' , name = ' Don Giovanni ' ,
address_line1 = " 100 Jackson St. " , address_line2 = " " , address_zip = " 94706 " , address_state = " CA " , address_country = None ) # good card
token1 = sc . create_token ( card = card1 )
# use the token id -- which is what we get from JavaScript api -- and retrieve the token
token2 = sc . token . retrieve ( id = token1 . id )
self . assertEqual ( token2 . id , token1 . id )
# make sure token id has a form tok_
self . assertEqual ( token2 . id [ : 4 ] , " tok_ " )
# should be only test mode
self . assertEqual ( token2 . livemode , False )
# token hasn't been used yet
self . assertEqual ( token2 . used , False )
# test that card info matches up with what was fed in.
for k in CARD_FIELDS_TO_COMPARE :
self . assertEqual ( token2 . card [ k ] , card1 [ k ] )
# last4
self . assertEqual ( token2 . card . last4 , TEST_CARDS [ 0 ] [ 0 ] [ - 4 : ] )
# fingerprint
self . assertGreaterEqual ( len ( token2 . card . fingerprint ) , 16 )
# now charge the token
charge1 = sc . create_charge ( 10 , ' usd ' , card = token2 . id )
self . assertEqual ( charge1 . amount , 1000 )
self . assertEqual ( charge1 . id [ : 3 ] , " ch_ " )
2013-06-21 22:14:51 +00:00
# dispute, failure_message, fee, fee_details
self . assertEqual ( charge1 . dispute , None )
2012-10-06 16:51:43 +00:00
self . assertEqual ( charge1 . failure_message , None )
self . assertEqual ( charge1 . fee , 59 )
self . assertEqual ( charge1 . refunded , False )
def test_error_creating_customer_with_declined_card ( self ) :
""" Test whether we can get a charge decline error """
sc = StripeClient ( )
card1 = card ( number = ERROR_TESTING [ ' CHARGE_DECLINE ' ] [ 0 ] )
try :
cust1 = sc . create_customer ( card = card1 , description = " This card should fail " )
self . fail ( " Attempt to create customer did not throw expected exception. " )
except stripe . CardError as e :
self . assertEqual ( e . code , " card_declined " )
2012-11-06 19:22:25 +00:00
self . assertEqual ( e . message , " Your card was declined " )
2012-10-06 16:51:43 +00:00
def test_charge_bad_cust ( self ) :
# expect the card to be declined -- and for us to get CardError
sc = StripeClient ( )
# bad card
card1 = card ( number = ERROR_TESTING [ ' BAD_ATTACHED_CARD ' ] [ 0 ] )
2012-10-08 20:08:28 +00:00
# attaching card should be ok
2012-10-06 16:51:43 +00:00
cust1 = sc . create_customer ( card = card1 , description = " test bad customer " , email = " rdhyee@gluejar.com " )
2012-10-08 20:08:28 +00:00
# trying to charge the card should fail
2012-10-06 16:51:43 +00:00
self . assertRaises ( stripe . CardError , sc . create_charge , 10 ,
customer = cust1 . id , description = " $10 for bad cust " )
2012-10-08 20:08:28 +00:00
def test_bad_cc_number ( self ) :
""" send a bad cc and should get an error when trying to create a token """
BAD_CC_NUM = ' 4242424242424241 '
# reason for decline is number is not Luhn valid
self . assertFalse ( is_luhn_valid ( BAD_CC_NUM ) )
sc = StripeClient ( )
card1 = card ( number = BAD_CC_NUM , exp_month = 1 , exp_year = 2020 , cvc = ' 123 ' , name = ' Don Giovanni ' ,
address_line1 = " 100 Jackson St. " , address_line2 = " " , address_zip = " 94706 " , address_state = " CA " , address_country = None ) # good card
try :
token1 = sc . create_token ( card = card1 )
self . fail ( " Attempt to create token with bad cc number did not throw expected exception. " )
except stripe . CardError as e :
self . assertEqual ( e . code , " incorrect_number " )
self . assertEqual ( e . message , " Your card number is incorrect " )
def test_invalid_expiry_month ( self ) :
""" Use an invalid month e.g. 13. """
sc = StripeClient ( )
card1 = card ( number = TEST_CARDS [ 0 ] [ 0 ] , exp_month = 13 , exp_year = 2020 , cvc = ' 123 ' , name = ' Don Giovanni ' ,
address_line1 = " 100 Jackson St. " , address_line2 = " " , address_zip = " 94706 " , address_state = " CA " , address_country = None )
2012-10-06 16:51:43 +00:00
2012-10-08 20:08:28 +00:00
try :
token1 = sc . create_token ( card = card1 )
self . fail ( " Attempt to create token with invalid expiry month did not throw expected exception. " )
except stripe . CardError as e :
self . assertEqual ( e . code , " invalid_expiry_month " )
self . assertEqual ( e . message , " Your card ' s expiration month is invalid " )
def test_invalid_expiry_year ( self ) :
""" Use a year in the past e.g. 1970. """
sc = StripeClient ( )
card1 = card ( number = TEST_CARDS [ 0 ] [ 0 ] , exp_month = 12 , exp_year = 1970 , cvc = ' 123 ' , name = ' Don Giovanni ' ,
address_line1 = " 100 Jackson St. " , address_line2 = " " , address_zip = " 94706 " , address_state = " CA " , address_country = None )
try :
token1 = sc . create_token ( card = card1 )
self . fail ( " Attempt to create token with invalid expiry year did not throw expected exception. " )
except stripe . CardError as e :
self . assertEqual ( e . code , " invalid_expiry_year " )
self . assertEqual ( e . message , " Your card ' s expiration year is invalid " )
def test_invalid_cvc ( self ) :
""" Use a two digit number e.g. 99. """
sc = StripeClient ( )
card1 = card ( number = TEST_CARDS [ 0 ] [ 0 ] , exp_month = 12 , exp_year = 2020 , cvc = ' 99 ' , name = ' Don Giovanni ' ,
address_line1 = " 100 Jackson St. " , address_line2 = " " , address_zip = " 94706 " , address_state = " CA " , address_country = None )
try :
token1 = sc . create_token ( card = card1 )
self . fail ( " Attempt to create token with invalid cvc did not throw expected exception. " )
except stripe . CardError as e :
self . assertEqual ( e . code , " invalid_cvc " )
self . assertEqual ( e . message , " Your card ' s security code is invalid " )
def test_missing_card ( self ) :
""" There is no card on a customer that is being charged """
sc = StripeClient ( )
# create a Customer with no attached card
cust1 = sc . create_customer ( description = " test cust w/ no card " )
try :
sc . create_charge ( 10 , customer = cust1 . id , description = " $10 for cust w/ no card " )
except stripe . CardError as e :
self . assertEqual ( e . code , " missing " )
self . assertEqual ( e . message , " Cannot charge a customer that has no active card " )
2012-11-05 18:44:24 +00:00
2012-08-30 23:51:59 +00:00
class PledgeScenarioTest ( TestCase ) :
2012-08-31 18:19:05 +00:00
@classmethod
def setUpClass ( cls ) :
cls . _sc = StripeClient ( api_key = STRIPE_SK )
# valid card
2012-08-30 23:51:59 +00:00
card0 = card ( )
2012-08-31 18:19:05 +00:00
cls . _good_cust = cls . _sc . create_customer ( card = card0 , description = " test good customer " , email = " raymond.yee@gmail.com " )
# bad card
2012-09-01 05:59:22 +00:00
test_card_num_to_get_BAD_ATTACHED_CARD = ERROR_TESTING [ ' BAD_ATTACHED_CARD ' ] [ 0 ]
card1 = card ( number = test_card_num_to_get_BAD_ATTACHED_CARD )
2012-08-31 18:19:05 +00:00
cls . _cust_bad_card = cls . _sc . create_customer ( card = card1 , description = " test bad customer " , email = " rdhyee@gluejar.com " )
2012-08-30 23:51:59 +00:00
def test_charge_good_cust ( self ) :
2012-09-19 00:56:38 +00:00
charge = self . _sc . create_charge ( 10 , customer = self . _good_cust . id , description = " $10 for good cust " )
2013-06-24 23:19:12 +00:00
self . assertEqual ( type ( charge . id ) , unicode )
2012-09-15 00:00:21 +00:00
# print out all the pieces of Customer and Charge objects
print dir ( charge )
print dir ( self . _good_cust )
2012-08-31 18:19:05 +00:00
2012-09-01 05:59:22 +00:00
def test_error_creating_customer_with_declined_card ( self ) :
# should get a CardError upon attempt to create Customer with this card
_card = card ( number = card ( ERROR_TESTING [ ' CHARGE_DECLINE ' ] [ 0 ] ) )
self . assertRaises ( stripe . CardError , self . _sc . create_customer , card = _card )
2012-08-31 18:19:05 +00:00
def test_charge_bad_cust ( self ) :
2012-09-01 05:59:22 +00:00
# expect the card to be declined -- and for us to get CardError
2012-08-31 18:19:05 +00:00
self . assertRaises ( stripe . CardError , self . _sc . create_charge , 10 ,
2012-09-19 00:56:38 +00:00
customer = self . _cust_bad_card . id , description = " $10 for bad cust " )
2012-09-15 00:00:21 +00:00
2012-08-31 18:19:05 +00:00
@classmethod
def tearDownClass ( cls ) :
2012-09-15 00:00:21 +00:00
# clean up stuff we create in test -- right now list current objects
2013-06-24 23:19:12 +00:00
pass
2012-09-15 00:00:21 +00:00
#cls._good_cust.delete()
2013-06-24 23:19:12 +00:00
#print "list of customers"
#print [(i, c.id, c.description, c.email, datetime.fromtimestamp(c.created, tz=utc), c.account_balance, c.delinquent, c.active_card.fingerprint, c.active_card.type, c.active_card.last4, c.active_card.exp_month, c.active_card.exp_year, c.active_card.country) for(i, c) in enumerate(cls._sc.customer.all()["data"])]
#
#print "list of charges"
#print [(i, c.id, c.amount, c.amount_refunded, c.currency, c.description, datetime.fromtimestamp(c.created, tz=utc), c.paid, c.fee, c.disputed, c.amount_refunded, c.failure_message, c.card.fingerprint, c.card.type, c.card.last4, c.card.exp_month, c.card.exp_year) for (i, c) in enumerate(cls._sc.charge.all()['data'])]
#
## can retrieve events since a certain time?
#print "list of events", cls._sc.event.all()
#print [(i, e.id, e.type, e.created, e.pending_webhooks, e.data) for (i,e) in enumerate(cls._sc.event.all()['data'])]
2012-08-30 23:51:59 +00:00
[#37053797] reverting stripelib.py -- Sn tests run but django command test_stripe_charge fails: done so Eric can fix circular import issue:
>>> >>> >>> (regluitdj14)raymond-yees-computer:regluit raymondyee$ django-admin.py test_stripe_charge
Traceback (most recent call last):
File "/Users/raymondyee/.virtualenvs/regluitdj14/bin/django-admin.py", line 5, in <module>
management.execute_from_command_line()
File "/Users/raymondyee/.virtualenvs/regluitdj14/lib/python2.7/site-packages/django/core/management/__init__.py", line 443, in execute_from_command_line
utility.execute()
File "/Users/raymondyee/.virtualenvs/regluitdj14/lib/python2.7/site-packages/django/core/management/__init__.py", line 382, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "/Users/raymondyee/.virtualenvs/regluitdj14/lib/python2.7/site-packages/django/core/management/__init__.py", line 261, in fetch_command
klass = load_command_class(app_name, subcommand)
File "/Users/raymondyee/.virtualenvs/regluitdj14/lib/python2.7/site-packages/django/core/management/__init__.py", line 69, in load_command_class
module = import_module('%s.management.commands.%s' % (app_name, name))
File "/Users/raymondyee/.virtualenvs/regluitdj14/lib/python2.7/site-packages/django/utils/importlib.py", line 35, in import_module
__import__(name)
File "/Users/raymondyee/C/src/Gluejar/regluit/payment/management/commands/test_stripe_charge.py", line 2, in <module>
from regluit.payment import stripelib
File "/Users/raymondyee/C/src/Gluejar/regluit/payment/stripelib.py", line 10, in <module>
from regluit.payment.models import Account
File "/Users/raymondyee/C/src/Gluejar/regluit/payment/models.py", line 4, in <module>
from regluit.core.models import Campaign, Wishlist, Premium, PledgeExtra
File "/Users/raymondyee/C/src/Gluejar/regluit/core/models.py", line 998, in <module>
from regluit.payment.manager import PaymentManager
File "/Users/raymondyee/C/src/Gluejar/regluit/payment/manager.py", line 2, in <module>
from regluit.payment.models import Transaction, Receiver, PaymentResponse
ImportError: cannot import name Transaction
2012-10-02 19:02:45 +00:00
class StripePaymentRequest ( baseprocessor . BasePaymentRequest ) :
""" so far there is no need to have a separate class here """
pass
2012-09-21 18:24:42 +00:00
2012-10-03 03:49:19 +00:00
class Processor ( baseprocessor . Processor ) :
2012-09-24 18:30:21 +00:00
2014-02-20 20:56:10 +00:00
def make_account ( self , user = None , token = None , email = None ) :
2012-10-03 03:49:19 +00:00
""" returns a payment.models.Account based on stripe token and user """
2012-09-22 00:40:33 +00:00
2012-11-14 19:42:52 +00:00
if token is None or len ( token ) == 0 :
2012-11-14 18:53:19 +00:00
raise StripelibError ( " input token is None " , None )
2012-10-13 17:45:46 +00:00
2012-09-22 00:40:33 +00:00
sc = StripeClient ( )
2012-10-13 17:45:46 +00:00
# create customer and charge id and then charge the customer
try :
2014-02-20 03:18:23 +00:00
if user :
customer = sc . create_customer ( card = token , description = user . username ,
2012-10-13 17:45:46 +00:00
email = user . email )
2014-02-20 03:18:23 +00:00
else :
2014-02-20 20:56:10 +00:00
customer = sc . create_customer ( card = token , description = ' anonymous user ' , email = email )
2012-10-13 17:45:46 +00:00
except stripe . StripeError as e :
raise StripelibError ( e . message , e )
account = Account ( host = PAYMENT_HOST_STRIPE ,
account_id = customer . id ,
card_last4 = customer . active_card . last4 ,
card_type = customer . active_card . type ,
card_exp_month = customer . active_card . exp_month ,
card_exp_year = customer . active_card . exp_year ,
card_fingerprint = customer . active_card . fingerprint ,
card_country = customer . active_card . country ,
user = user
)
2014-02-20 03:18:23 +00:00
if user and user . profile . account :
2012-10-15 03:41:17 +00:00
user . profile . account . deactivate ( )
2013-08-05 18:37:46 +00:00
account . save ( )
account . recharge_failed_transactions ( )
else :
account . save ( )
2012-10-03 03:49:19 +00:00
return account
2012-09-22 00:40:33 +00:00
2012-10-03 03:49:19 +00:00
class Preapproval ( StripePaymentRequest , baseprocessor . Processor . Preapproval ) :
2012-09-24 23:29:20 +00:00
2012-10-03 03:49:19 +00:00
def __init__ ( self , transaction , amount , expiry = None , return_url = None , paymentReason = " " ) :
# set the expiration date for the preapproval if not passed in. This is what the paypal library does
self . transaction = transaction
now_val = now ( )
if expiry is None :
expiry = now_val + timedelta ( days = settings . PREAPPROVAL_PERIOD )
transaction . date_authorized = now_val
transaction . date_expired = expiry
# let's figure out what part of transaction can be used to store info
# try placing charge id in transaction.pay_key
# need to set amount
# how does transaction.max_amount get set? -- coming from /pledge/xxx/ -> manager.process_transaction
# max_amount is set -- but I don't think we need it for stripe
2012-09-21 18:24:42 +00:00
2012-10-03 03:49:19 +00:00
# ASSUMPTION: a user has any given moment one and only one active payment Account
2012-09-21 18:24:42 +00:00
2012-10-13 17:45:46 +00:00
account = transaction . user . profile . account
if not account :
2012-10-03 03:49:19 +00:00
logger . warning ( " user {0} has no active payment account " . format ( transaction . user ) )
2012-10-06 16:51:43 +00:00
raise StripelibError ( " user {0} has no active payment account " . format ( transaction . user ) )
2012-10-03 03:49:19 +00:00
logger . info ( " user: {0} customer.id is {1} " . format ( transaction . user , account . account_id ) )
# settings to apply to transaction for TRANSACTION_STATUS_ACTIVE
# should approved be set to False and wait for a webhook?
transaction . approved = True
transaction . type = PAYMENT_TYPE_AUTHORIZATION
transaction . host = PAYMENT_HOST_STRIPE
transaction . status = TRANSACTION_STATUS_ACTIVE
2012-09-21 18:24:42 +00:00
2012-10-03 03:49:19 +00:00
transaction . preapproval_key = account . account_id
transaction . currency = ' USD '
transaction . amount = amount
transaction . save ( )
def key ( self ) :
return self . transaction . preapproval_key
2012-09-21 18:24:42 +00:00
2012-10-03 03:49:19 +00:00
def next_url ( self ) :
""" return None because no redirection to stripe is required """
return None
2013-06-20 01:54:32 +00:00
class Pay ( StripePaymentRequest , baseprocessor . Processor . Pay ) :
'''
The pay function generates a redirect URL to approve the transaction
2014-02-20 03:18:23 +00:00
If the transaction has a null user ( is_anonymous ) , then a token musr be supplied
2013-06-20 01:54:32 +00:00
'''
2014-02-20 03:18:23 +00:00
def __init__ ( self , transaction , return_url = None , amount = None , paymentReason = " " , token = None ) :
2013-06-27 01:46:30 +00:00
self . transaction = transaction
self . url = return_url
now_val = now ( )
transaction . date_authorized = now_val
# ASSUMPTION: a user has any given moment one and only one active payment Account
2014-02-20 03:18:23 +00:00
if token :
# user is anonymous
2014-02-20 20:56:10 +00:00
account = transaction . get_payment_class ( ) . make_account ( token = token , email = transaction . receipt )
2014-02-20 03:18:23 +00:00
else :
account = transaction . user . profile . account
2013-06-27 01:46:30 +00:00
if not account :
logger . warning ( " user {0} has no active payment account " . format ( transaction . user ) )
raise StripelibError ( " user {0} has no active payment account " . format ( transaction . user ) )
logger . info ( " user: {0} customer.id is {1} " . format ( transaction . user , account . account_id ) )
# settings to apply to transaction for TRANSACTION_STATUS_ACTIVE
# should approved be set to False and wait for a webhook?
transaction . approved = True
transaction . type = PAYMENT_TYPE_INSTANT
transaction . host = PAYMENT_HOST_STRIPE
transaction . preapproval_key = account . account_id
transaction . currency = ' USD '
transaction . amount = amount
transaction . save ( )
# execute the transaction
p = transaction . get_payment_class ( ) . Execute ( transaction )
if p . success ( ) and not p . error ( ) :
transaction . pay_key = p . key ( )
transaction . save ( )
else :
2013-11-25 18:01:16 +00:00
self . errorMessage = p . errorMessage #pass error message up
2013-06-27 01:46:30 +00:00
logger . info ( " execute_transaction Error: " + p . error_string ( ) )
2013-06-25 22:34:20 +00:00
2013-06-20 01:54:32 +00:00
def amount ( self ) :
return self . transaction . amount
def key ( self ) :
2013-06-27 01:46:30 +00:00
return self . transaction . pay_key
2013-06-20 01:54:32 +00:00
def next_url ( self ) :
return self . url
2012-10-03 03:49:19 +00:00
class Execute ( StripePaymentRequest ) :
2012-09-21 18:24:42 +00:00
2012-10-03 03:49:19 +00:00
'''
2014-02-20 03:18:23 +00:00
The Execute function attempts to charge the credit card of stripe Customer associated with user connected to transaction .
2012-10-03 03:49:19 +00:00
'''
2012-09-21 18:24:42 +00:00
2012-10-03 03:49:19 +00:00
def __init__ ( self , transaction = None ) :
2013-11-25 18:01:16 +00:00
2012-10-03 03:49:19 +00:00
self . transaction = transaction
2012-09-21 18:24:42 +00:00
2013-08-22 18:03:22 +00:00
# make sure transaction hasn't already been executed
if transaction . status == TRANSACTION_STATUS_COMPLETE :
return
2012-10-31 15:23:13 +00:00
# make sure we are dealing with a stripe transaction
if transaction . host < > PAYMENT_HOST_STRIPE :
2013-11-24 23:44:40 +00:00
raise StripelibError ( " transaction.host {0} is not the expected {1} " . format ( transaction . host , PAYMENT_HOST_STRIPE ) )
2012-10-03 03:49:19 +00:00
sc = StripeClient ( )
2012-10-31 15:23:13 +00:00
# look first for transaction.user.profile.account.account_id
try :
customer_id = transaction . user . profile . account . account_id
except :
customer_id = transaction . preapproval_key
2012-10-03 03:49:19 +00:00
2012-10-31 15:23:13 +00:00
if customer_id is not None :
2012-10-06 16:51:43 +00:00
try :
2012-10-31 15:23:13 +00:00
# useful things to put in description: transaction.id, transaction.user.id, customer_id, transaction.amount
charge = sc . create_charge ( transaction . amount , customer = customer_id ,
2012-11-05 18:44:24 +00:00
description = json . dumps ( { " t.id " : transaction . id ,
2014-02-20 03:18:23 +00:00
" email " : transaction . user . email if transaction . user else transaction . receipt ,
2012-11-05 18:44:24 +00:00
" cus.id " : customer_id ,
2017-02-13 18:33:26 +00:00
" tc.id " : transaction . campaign . id if transaction . campaign else ' 0 ' ,
2012-11-05 18:44:24 +00:00
" amount " : float ( transaction . amount ) } ) )
2012-10-06 16:51:43 +00:00
except stripe . StripeError as e :
2012-11-20 17:28:10 +00:00
# what to record in terms of errors? (error log?)
# use PaymentResponse to store error
2012-10-31 15:23:13 +00:00
2012-11-20 17:28:10 +00:00
r = PaymentResponse . objects . create ( api = " stripelib.Execute " , correlation_id = None ,
2012-11-20 21:56:14 +00:00
timestamp = now ( ) , info = e . message ,
status = TRANSACTION_STATUS_ERROR , transaction = transaction )
2012-10-31 15:23:13 +00:00
2012-11-20 21:56:14 +00:00
transaction . status = TRANSACTION_STATUS_ERROR
2013-11-25 18:01:16 +00:00
self . errorMessage = e . message # manager puts this on transaction
2012-11-20 21:56:14 +00:00
transaction . save ( )
2012-11-30 00:24:51 +00:00
# fire off the fact that transaction failed -- should actually do so only if not a transient error
# if card_declined or expired card, ask user to update account
2013-08-20 02:52:22 +00:00
if isinstance ( e , stripe . CardError ) and e . code in ( ' card_declined ' , ' expired_card ' , ' incorrect_number ' , ' processing_error ' ) :
2012-11-30 00:24:51 +00:00
transaction_failed . send ( sender = self , transaction = transaction )
# otherwise, report exception to us
else :
logger . exception ( " transaction id {0} , exception: {1} " . format ( transaction . id , e . message ) )
2013-11-25 18:01:16 +00:00
# raise StripelibError(e.message, e)
2012-10-06 16:51:43 +00:00
2012-10-31 15:23:13 +00:00
else :
self . charge = charge
transaction . status = TRANSACTION_STATUS_COMPLETE
transaction . pay_key = charge . id
transaction . date_payment = now ( )
transaction . save ( )
2012-11-30 00:24:51 +00:00
# fire signal for sucessful transaction
transaction_charged . send ( sender = self , transaction = transaction )
2012-10-31 15:23:13 +00:00
else :
# nothing to charge
2013-11-24 23:44:40 +00:00
raise StripelibError ( " No customer id available to charge for transaction {0} " . format ( transaction . id ) , None )
2012-10-31 15:23:13 +00:00
2012-09-21 18:24:42 +00:00
2012-10-03 03:49:19 +00:00
def api ( self ) :
return " Base Pay "
def key ( self ) :
# IN paypal land, our key is updated from a preapproval to a pay key here, just return the existing key
return self . transaction . pay_key
class PreapprovalDetails ( StripePaymentRequest ) :
'''
Get details about an authorized token
This api must set 4 different class variables to work with the code in manager . py
status - one of the global transaction status codes
approved - boolean value
currency - not used in this API , but we can get some more info via other APIs - TODO
amount - not used in this API , but we can get some more info via other APIs - TODO
'''
def __init__ ( self , transaction ) :
self . transaction = transaction
self . status = self . transaction . status
if self . status == TRANSACTION_STATUS_CANCELED :
self . approved = False
else :
self . approved = True
2012-09-27 16:32:29 +00:00
2012-10-03 03:49:19 +00:00
# Set the other fields that are expected. We don't have values for these now, so just copy the transaction
self . currency = transaction . currency
self . amount = transaction . amount
2012-11-05 18:44:24 +00:00
2012-10-15 04:16:15 +00:00
def ProcessIPN ( self , request ) :
2012-10-22 14:50:52 +00:00
# retrieve the request's body and parse it as JSON in, e.g. Django
try :
event_json = json . loads ( request . body )
2012-10-23 22:18:03 +00:00
except ValueError , e :
2012-10-22 14:50:52 +00:00
# not able to parse request.body -- throw a "Bad Request" error
2012-10-23 22:18:03 +00:00
logger . warning ( " Non-json being sent to Stripe IPN: {0} " . format ( e ) )
2012-10-22 14:50:52 +00:00
return HttpResponse ( status = 400 )
else :
2012-10-22 15:25:47 +00:00
# now parse out pieces of the webhook
event_id = event_json . get ( " id " )
# use Stripe to ask for details -- ignore what we're sent for security
sc = StripeClient ( )
try :
event = sc . event . retrieve ( event_id )
except stripe . InvalidRequestError :
2012-10-23 22:18:03 +00:00
logger . warning ( " Invalid Event ID: {0} " . format ( event_id ) )
2012-10-22 15:25:47 +00:00
return HttpResponse ( status = 400 )
else :
event_type = event . get ( " type " )
2012-11-05 18:44:24 +00:00
if event_type not in STRIPE_EVENT_TYPES :
logger . warning ( " Unrecognized Stripe event type {0} for event {1} " . format ( event_type , event_id ) )
# is this the right code to respond with?
return HttpResponse ( status = 400 )
2012-10-23 16:29:47 +00:00
# https://stripe.com/docs/api?lang=python#event_types -- type to delegate things
# parse out type as resource.action
2012-11-05 18:44:24 +00:00
2012-10-23 16:29:47 +00:00
try :
2012-11-05 18:44:24 +00:00
( resource , action ) = re . match ( " ^(.+) \ .([^ \ .]*)$ " , event_type ) . groups ( )
2012-10-23 22:18:03 +00:00
except Exception , e :
logger . warning ( " Parsing of event_type into resource, action failed: {0} " . format ( e ) )
2012-10-23 16:29:47 +00:00
return HttpResponse ( status = 400 )
2012-10-30 18:17:09 +00:00
try :
2012-11-05 18:44:24 +00:00
ev_object = event . data . object
2012-10-30 18:17:09 +00:00
except Exception , e :
logger . warning ( " attempt to retrieve event object failed: {0} " . format ( e ) )
return HttpResponse ( status = 400 )
2012-10-23 16:29:47 +00:00
if event_type == ' account.updated ' :
# should we alert ourselves?
# how about account.application.deauthorized ?
pass
elif resource == ' charge ' :
# we need to handle: succeeded, failed, refunded, disputed
2012-11-30 00:24:51 +00:00
2012-10-30 18:17:09 +00:00
if action == ' succeeded ' :
2012-11-30 00:24:51 +00:00
# TO DO: delete this logic since we don't do anything but look up transaction.
2012-10-30 18:17:09 +00:00
logger . info ( " charge.succeeded webhook for {0} " . format ( ev_object . get ( " id " ) ) )
2012-11-05 18:44:24 +00:00
# try to parse description of object to pull related transaction if any
# wrapping this in a try statement because it possible that we have a charge.succeeded outside context of unglue.it
try :
charge_meta = json . loads ( ev_object [ " description " ] )
transaction = Transaction . objects . get ( id = charge_meta [ " t.id " ] )
# now check that account associated with the transaction matches
# ev.data.object.id, t.pay_key
if ev_object . id == transaction . pay_key :
logger . info ( " ev_object.id == transaction.pay_key: {0} " . format ( ev_object . id ) )
else :
logger . warning ( " ev_object.id {0} <> transaction.pay_key {1} " . format ( ev_object . id , transaction . pay_key ) )
except Exception , e :
2012-11-30 00:24:51 +00:00
logger . warning ( e )
2012-11-05 18:44:24 +00:00
2012-10-30 18:17:09 +00:00
elif action == ' failed ' :
2012-11-30 00:24:51 +00:00
# TO DO: delete this logic since we don't do anything but look up transaction.
2012-11-06 19:22:25 +00:00
logger . info ( " charge.failed webhook for {0} " . format ( ev_object . get ( " id " ) ) )
try :
charge_meta = json . loads ( ev_object [ " description " ] )
transaction = Transaction . objects . get ( id = charge_meta [ " t.id " ] )
# now check that account associated with the transaction matches
# ev.data.object.id, t.pay_key
if ev_object . id == transaction . pay_key :
logger . info ( " ev_object.id == transaction.pay_key: {0} " . format ( ev_object . id ) )
else :
logger . warning ( " ev_object.id {0} <> transaction.pay_key {1} " . format ( ev_object . id , transaction . pay_key ) )
2012-11-30 00:24:51 +00:00
2012-11-06 19:22:25 +00:00
except Exception , e :
logger . warning ( e )
2012-10-30 18:17:09 +00:00
elif action == ' refunded ' :
pass
elif action == ' disputed ' :
pass
else :
2012-11-05 18:44:24 +00:00
# unexpected
2012-10-30 18:17:09 +00:00
pass
2012-10-23 16:29:47 +00:00
elif resource == ' customer ' :
2012-10-23 22:18:03 +00:00
if action == ' created ' :
2012-10-23 21:20:13 +00:00
# test application: email Raymond
# do we have a flag to indicate production vs non-production? -- or does it matter?
2012-10-30 18:17:09 +00:00
# email RY whenever a new Customer created -- we probably want to replace this with some other
# more useful long tem action.
2015-04-21 15:55:27 +00:00
send_mail ( u " Stripe Customer (id {0} ; description: {1} ) created " . format ( ev_object . get ( " id " ) ,
ev_object . get ( " description " ) ) ,
u " Stripe Customer email: {0} " . format ( ev_object . get ( " email " ) ) ,
2012-10-30 18:17:09 +00:00
" notices@gluejar.com " ,
[ " rdhyee@gluejar.com " ] )
logger . info ( " email sent for customer.created for {0} " . format ( ev_object . get ( " id " ) ) )
2012-10-23 21:20:13 +00:00
# handle updated, deleted
else :
pass
2012-10-30 18:17:09 +00:00
else : # other events
2012-10-23 16:29:47 +00:00
pass
2012-10-22 15:25:47 +00:00
return HttpResponse ( " event_id: {0} event_type: {1} " . format ( event_id , event_type ) )
2012-10-15 04:16:15 +00:00
2012-09-21 18:24:42 +00:00
2012-08-30 23:51:59 +00:00
def suite ( ) :
2013-06-24 23:19:12 +00:00
testcases = [ PledgeScenarioTest , StripeErrorTest ]
#testcases = [StripeErrorTest]
2012-08-30 23:51:59 +00:00
suites = unittest . TestSuite ( [ unittest . TestLoader ( ) . loadTestsFromTestCase ( testcase ) for testcase in testcases ] )
#suites.addTest(LibraryThingTest('test_cache'))
#suites.addTest(SettingsTest('test_dev_me_alignment')) # give option to test this alignment
return suites
2012-08-17 00:24:18 +00:00
# IPNs/webhooks: https://stripe.com/docs/webhooks
2012-08-30 23:51:59 +00:00
# how to use pending_webhooks ?
2012-08-17 00:24:18 +00:00
# all events
# https://stripe.com/docs/api?lang=python#list_events
2012-08-30 23:51:59 +00:00
if __name__ == ' __main__ ' :
#unittest.main()
suites = suite ( )
#suites = unittest.defaultTestLoader.loadTestsFromModule(__import__('__main__'))
unittest . TextTestRunner ( ) . run ( suites )