5 sys
.path
.insert
(0, '/afs/hcoop.net/user/h/hc/hcoop/portal3/stripe/stripe-pkg/lib/python2.6/site-packages/')
7 import stripe
, cgi
, psycopg2
, cgitb
, datetime
, smtplib
8 from email
.mime
.text import MIMEText
9 from contextlib import contextmanager
13 def notify_payment
(charge
, member
):
15 A member has paid us via Stripe. Please visit the portal money
16 page at your earliest convenience to process the payment.
21 """.format
(member
, charge
.amount
, charge
.id
)
23 msg
= MIMEText
(msg_text
)
24 msg
['Subject'] = 'Stripe payment received from {0}'.format
(member
)
25 msg
['From'] = 'payment@hcoop.net'
26 msg
['To'] = 'payment@hcoop.net'
28 s
= smtplib
.SMTP
('mail.hcoop.net')
29 s
.sendmail
('payment@hcoop.net', ['payment@hcoop.net'], msg
.as_string
())
32 def notify_payment_rejected
(charge
, reason
):
33 # TODO: notify member...
34 msg_text
= """We have rejected a payment from a member.
38 """.format
(charge
.amount
, charge
.id
, reason
)
40 msg
= MIMEText
(msg_text
)
41 msg
['Subject'] = 'Stripe payment rejected'
42 msg
['From'] = 'payment@hcoop.net'
43 msg
['To'] = 'payment@hcoop.net'
45 s
= smtplib
.SMTP
('mail.hcoop.net')
46 s
.sendmail
('payment@hcoop.net', ['payment@hcoop.net'], msg
.as_string
())
50 keyfile
= open ("/afs/hcoop.net/user/h/hc/hcoop/.portal-private/stripe", "r")
51 keystring
= keyfile
.read ()
57 def stripe_error_handling
():
60 except stripe
.error
.CardError
, e
: # The card has been declined
61 print 'Status: 200 OK'
64 print '<head><title>Transaction Failed</title></head>'
66 print '<h1>Failed</h1><p>Reason: '
67 print e
.json_body
['error']['message']
72 except stripe
.error
.StripeError
, e
: # General stripe failure
73 print 'Status: 200 OK'
76 print '<head><title>Stripe Error</title></head>'
78 print '<h1>Failed</h1><p>Reason: '
79 print e
.json_body
['error']['message']
86 def stripe_refund_on_error
(charge
):
90 print 'Status: 200 OK'
91 print 'Content-Type: text/html'
93 print '<h1>Something went wrong after accepting payment!</h1>'
95 print '<p>The charge should be refunded. Please contact payment@hcoop.net if it was not!</p>'
98 def stripe_success
(redirect_to
):
99 print 'Status: 303 See Other'
100 print 'Location: {0}'.format
(redirect_to
);
102 print '<a href="{0}">Go back to the portal</a>'.format
(redirect_to
)
104 # Set your secret key: remember to change this to your live secret key in production
105 # See your keys here https://manage.stripe.com/account
107 stripe
.api_key
= stripe_key
()
108 connstring
= 'dbname=hcoop_portal3test user=hcoop host=postgres port=5433'
110 # Get the credit card details submitted by the form
112 request_params
= cgi
.FieldStorage
()
113 request_command
= request_params
.getvalue
('cmd', 'none');
115 assert request_command
!= 'none', 'No command given.'
117 # Create the charge on Stripe's servers - this will charge the user's card
119 if request_command
== 'member_payment':
120 token
= request_params
.getvalue
('stripeToken')
121 webuser_id
= request_params
.getvalue
('webuser_id')
122 member_name
= request_params
.getvalue
('webuser_name')
123 amount
= request_params
.getvalue
('stripeDues')
125 with stripe_error_handling
():
126 charge
= stripe
.Charge
.create
( amount
=amount
,
129 description
='Payment for member {0}'.format
(member_name
))
131 with stripe_refund_on_error
(charge
):
132 # assert charge.card.address_line1_check == 'pass', 'Address verification failed or unknown.'
133 assert charge
.card
.cvc_check
== 'pass', 'CVC verification failed or unknown.'
134 # assert charge.card.address_zip_check == 'pass', 'Zipcode verification failed or unknown.'
136 balance
= stripe
.BalanceTransaction
.retrieve
(charge
.balance_transaction
)
137 conn
= psycopg2
.connect ('dbname=hcoop_portal3test user=hcoop host=postgres port=5433')
139 cur
.execute
('insert into stripe_payment (charge_id, card_name, webuser_id, paid_on, gross, fee) values (%s, %s, %s, %s, %s, %s)',
140 (charge
.id
, charge
.card
.name
, webuser_id
, datetime
.date
.today
(), charge
.amount
, balance
.fee
))
143 notify_payment
(charge
, member_name
)
144 stripe_success
('/portal/portal?cmd=stripeSuccess')
145 elif request_command
== 'reject_member_payment':
146 # todo: protect command using group file and separate cgi
147 charge_id
= request_params
.getvalue
('stripeChargeId');
148 reason
= request_params
.getvalue
('reason', 'none given');
149 with stripe_error_handling
():
150 conn
= psycopg2
.connect (connstring
)
153 cur
.execute
('SELECT charge_id FROM stripe_payment WHERE charge_id = %s', (charge_id
,))
154 assert cur
.fetchone
() != None
, 'Bad charge id'
155 cur
.execute
('SELECT stripe_charge_id FROM stripe_handled WHERE stripe_charge_id = %s', (charge_id
,))
156 assert cur
.fetchone
() == None
, 'Cannot rejected a previously handled payment'
158 charge
= stripe
.Charge
.retrieve
(charge_id
);
160 cur
.execute
('insert into stripe_rejected (stripe_charge_id, refunded_on, reason) values (%s, %s, %s)',
161 (charge
.id
, datetime
.date
.today
(), reason
))
164 notify_payment_rejected
(charge
, reason
)
165 stripe_success
('/portal/money?cmd=stripeRejected')
167 assert False
, 'Invalid command.'
169 # Use mod_authz_groupfile to store money/root
170 # (All hcoop members should be able to use this!)
171 # [support Satisfy? Satisfy: all is OK for now...]
172 # Whenever groups are updated in the portal, write the file
173 # make sure to store the file outside of the web root (duh)
174 # only users in money/root can do reject/adduser
175 # common code should go into a module (feh!)
176 # application_payment in one cgi (anyone)
177 # member_payment in another (only kerberos users)
178 # reject_payment / capture_application_payment (kerberos + inGroup {money, root})
180 # If there is a way to allow all and check the group info
181 # here... maybe investigate, but beware security holes
182 # alt: libapache2-mod-authnz-external + db helper script
183 # can use ExternalGroup, check kerberos user is in group specified in