Split stripe payment/admin scripts
authorClinton Ebadi <clinton@unknownlamer.org>
Sat, 29 Mar 2014 09:57:49 +0000 (05:57 -0400)
committerClinton Ebadi <clinton@unknownlamer.org>
Sat, 29 Mar 2014 09:57:49 +0000 (05:57 -0400)
Use an ugly hack of a module for common code. We are restricting
access to stripe-admin.cgi using apache and a group file. The portal
should generate the group file.

money.mlt
payment.mlt
stripe/hcoopstripe.py [new file with mode: 0644]
stripe/stripe-admin.cgi [new file with mode: 0755]
stripe/stripe-payment.cgi

index 63bfe0d..abd34a8 100644 (file)
--- a/money.mlt
+++ b/money.mlt
@@ -298,7 +298,7 @@ end %></textarea></td> </tr>
         showNormal := false;
 %>
 <h3>Are you sure you want to reject Stripe Payment <% $"stripeChargeId" %>?</h3>
-<form method="post" action="/stripe/stripe-payment.cgi">
+<form method="post" action="/stripe/stripe-admin.cgi">
         <input type="hidden" name="cmd" value="reject_member_payment" />
         <input type="hidden" name="stripeChargeId" value="<% $"stripeChargeId" %>" />
        <label>Reason for rejection <input type="text" name="reason" /></label>
index 73a6243..cbff0c4 100644 (file)
@@ -76,6 +76,7 @@ end %>
   <label>$<input type="text" name="stripeDues" id="stripeDues" pattern="^\\d+\\.\\d\\d$" required="required" size="7" /></label>
   <input type="hidden" name="webuser_id" value="<% #id you %>" />
   <input type="hidden" name="webuser_name" value="<% #name you %>" />
+  <input type="hidden" name="cmd" value="member_payment" />
   
   <button id="stripePay">Add To Balance</button>
   <script>
diff --git a/stripe/hcoopstripe.py b/stripe/hcoopstripe.py
new file mode 100644 (file)
index 0000000..49a4c91
--- /dev/null
@@ -0,0 +1,105 @@
+# -*- python -*-
+# Copyright (C) 2014 by Clinton Ebadi <clinton@unknownlamer.org>
+
+import sys
+sys.path.insert(0, '/afs/hcoop.net/user/h/hc/hcoop/portal3/stripe/stripe-pkg/lib/python2.6/site-packages/')
+
+import stripe, cgi, psycopg2, cgitb, datetime, smtplib
+from email.mime.text import MIMEText
+from contextlib import contextmanager
+
+def notify_payment (charge, member):
+    msg_text = """
+A member has paid us via Stripe. Please visit the portal money
+page at your earliest convenience to process the payment.
+
+  Member          : {0}
+  Amount (cents)  : {1}
+  Stripe Charge ID: {2}
+""".format (member, charge.amount, charge.id)
+
+    msg = MIMEText(msg_text)
+    msg['Subject'] = 'Stripe payment received from {0}'.format(member)
+    msg['From'] = 'payment@hcoop.net'
+    msg['To'] = 'payment@hcoop.net'
+
+    s = smtplib.SMTP ('mail.hcoop.net')
+    s.sendmail ('payment@hcoop.net', ['payment@hcoop.net'], msg.as_string ())
+    s.quit ()
+
+def notify_payment_rejected (charge, reason):
+    # TODO: notify member...
+    msg_text = """We have rejected a payment from a member.
+  Amount:           {0}
+  Stripe Charge ID: {1}
+  Reason: {2}
+""".format (charge.amount, charge.id, reason)
+
+    msg = MIMEText(msg_text)
+    msg['Subject'] = 'Stripe payment rejected'
+    msg['From'] = 'payment@hcoop.net'
+    msg['To'] = 'payment@hcoop.net'
+
+    s = smtplib.SMTP ('mail.hcoop.net')
+    s.sendmail ('payment@hcoop.net', ['payment@hcoop.net'], msg.as_string ())
+    s.quit ()
+
+def stripe_key ():
+    keyfile = open ("/afs/hcoop.net/user/h/hc/hcoop/.portal-private/stripe", "r")
+    keystring = keyfile.read ()
+    keyfile.close ()
+    return keystring
+
+@contextmanager
+def stripe_error_handling ():
+    try:
+        yield
+    except stripe.error.CardError, e: # The card has been declined
+        print 'Status: 200 OK'
+        print
+        print '<html>'
+        print '<head><title>Transaction Failed</title></head>'
+        print '<body>'
+        print '<h1>Failed</h1><p>Reason: '
+        print e.json_body['error']['message']
+        print '</p>'
+        print '</body>'
+        print '</html>'
+        raise
+    except stripe.error.StripeError, e: # General stripe failure
+        print 'Status: 200 OK'
+        print
+        print '<html>'
+        print '<head><title>Stripe Error</title></head>'
+        print '<body>'
+        print '<h1>Failed</h1><p>Reason: '
+        print e.json_body['error']['message']
+        print '</p>'
+        print '</body>'
+        print '</html>'
+        raise
+
+@contextmanager
+def stripe_refund_on_error (charge):
+    try:
+        yield
+    except:
+        print 'Status: 200 OK'
+        print 'Content-Type: text/html'
+        print ''
+        print '<h1>Something went wrong after accepting payment!</h1>'
+        charge.refund ()
+        print '<p>The charge should be refunded. Please contact payment@hcoop.net if it was not!</p>'
+        raise
+
+def stripe_success (redirect_to):
+    print 'Status: 303 See Other'
+    print 'Location: {0}'.format(redirect_to);
+    print ''
+    print '<a href="{0}">Go back to the portal</a>'.format(redirect_to)
+
+connstring = 'dbname=hcoop_portal3test user=hcoop host=postgres port=5433'
+
+def hcoop_stripe_init ():
+    cgitb.enable()
+    stripe.api_key = stripe_key ()
diff --git a/stripe/stripe-admin.cgi b/stripe/stripe-admin.cgi
new file mode 100755 (executable)
index 0000000..250afc5
--- /dev/null
@@ -0,0 +1,39 @@
+#!/usr/bin/env python
+# -*- python -*-
+
+# Copyright (C) 2014 by Clinton Ebadi <clinton@unknownlamer.org>
+
+from hcoopstripe import *
+import stripe, cgi, psycopg2, cgitb, datetime
+
+hcoop_stripe_init ()
+
+request_params = cgi.FieldStorage()
+request_command = request_params.getvalue ('cmd', 'none');
+
+assert request_command != 'none', 'No command given.'
+
+hcoop_stripe_init ()
+
+if request_command == 'reject_member_payment':
+    charge_id = request_params.getvalue ('stripeChargeId');
+    reason = request_params.getvalue ('reason', 'none given');
+    with stripe_error_handling ():
+        conn = psycopg2.connect (connstring)
+        cur = conn.cursor ()
+
+        cur.execute ('SELECT charge_id FROM stripe_payment WHERE charge_id = %s', (charge_id,))
+        assert cur.fetchone() != None, 'Bad charge id'
+        cur.execute ('SELECT stripe_charge_id FROM stripe_handled WHERE stripe_charge_id = %s', (charge_id,))
+        assert cur.fetchone() == None, 'Cannot rejected a previously handled payment'
+        
+        charge = stripe.Charge.retrieve (charge_id);
+        charge.refund ()
+        cur.execute ('insert into stripe_rejected (stripe_charge_id, refunded_on, reason) values (%s, %s, %s)',
+                     (charge.id, datetime.date.today (), reason))
+        conn.commit ()
+
+    notify_payment_rejected (charge, reason)
+    stripe_success ('/portal/money?cmd=stripeRejected')
+else:
+    assert False, 'Invalid command.'
dissimilarity index 61%
index a6cd3e4..67fa904 100755 (executable)
-#!/usr/bin/env python
-# -*- python -*-
-
-import sys
-sys.path.insert(0, '/afs/hcoop.net/user/h/hc/hcoop/portal3/stripe/stripe-pkg/lib/python2.6/site-packages/')
-
-import stripe, cgi, psycopg2, cgitb, datetime, smtplib
-from email.mime.text import MIMEText
-from contextlib import contextmanager
-
-cgitb.enable()
-
-def notify_payment (charge, member):
-    msg_text = """
-A member has paid us via Stripe. Please visit the portal money
-page at your earliest convenience to process the payment.
-
-  Member          : {0}
-  Amount (cents)  : {1}
-  Stripe Charge ID: {2}
-""".format (member, charge.amount, charge.id)
-
-    msg = MIMEText(msg_text)
-    msg['Subject'] = 'Stripe payment received from {0}'.format(member)
-    msg['From'] = 'payment@hcoop.net'
-    msg['To'] = 'payment@hcoop.net'
-
-    s = smtplib.SMTP ('mail.hcoop.net')
-    s.sendmail ('payment@hcoop.net', ['payment@hcoop.net'], msg.as_string ())
-    s.quit ()
-
-def notify_payment_rejected (charge, reason):
-    # TODO: notify member...
-    msg_text = """We have rejected a payment from a member.
-  Amount:           {0}
-  Stripe Charge ID: {1}
-  Reason: {2}
-""".format (charge.amount, charge.id, reason)
-
-    msg = MIMEText(msg_text)
-    msg['Subject'] = 'Stripe payment rejected'
-    msg['From'] = 'payment@hcoop.net'
-    msg['To'] = 'payment@hcoop.net'
-
-    s = smtplib.SMTP ('mail.hcoop.net')
-    s.sendmail ('payment@hcoop.net', ['payment@hcoop.net'], msg.as_string ())
-    s.quit ()
-
-def stripe_key ():
-    keyfile = open ("/afs/hcoop.net/user/h/hc/hcoop/.portal-private/stripe", "r")
-    keystring = keyfile.read ()
-    keyfile.close ()
-    return keystring
-
-
-@contextmanager
-def stripe_error_handling ():
-    try:
-        yield
-    except stripe.error.CardError, e: # The card has been declined
-        print 'Status: 200 OK'
-        print
-        print '<html>'
-        print '<head><title>Transaction Failed</title></head>'
-        print '<body>'
-        print '<h1>Failed</h1><p>Reason: '
-        print e.json_body['error']['message']
-        print '</p>'
-        print '</body>'
-        print '</html>'
-        raise
-    except stripe.error.StripeError, e: # General stripe failure
-        print 'Status: 200 OK'
-        print
-        print '<html>'
-        print '<head><title>Stripe Error</title></head>'
-        print '<body>'
-        print '<h1>Failed</h1><p>Reason: '
-        print e.json_body['error']['message']
-        print '</p>'
-        print '</body>'
-        print '</html>'
-        raise
-
-@contextmanager
-def stripe_refund_on_error (charge):
-    try:
-        yield
-    except:
-        print 'Status: 200 OK'
-        print 'Content-Type: text/html'
-        print ''
-        print '<h1>Something went wrong after accepting payment!</h1>'
-        charge.refund ()
-        print '<p>The charge should be refunded. Please contact payment@hcoop.net if it was not!</p>'
-        raise
-
-def stripe_success (redirect_to):
-    print 'Status: 303 See Other'
-    print 'Location: {0}'.format(redirect_to);
-    print ''
-    print '<a href="{0}">Go back to the portal</a>'.format(redirect_to)
-
-# Set your secret key: remember to change this to your live secret key in production 
-# See your keys here https://manage.stripe.com/account
-
-stripe.api_key = stripe_key ()
-connstring = 'dbname=hcoop_portal3test user=hcoop host=postgres port=5433'
-
-# Get the credit card details submitted by the form
-
-request_params = cgi.FieldStorage()
-request_command = request_params.getvalue ('cmd', 'none');
-
-assert request_command != 'none', 'No command given.'
-
-# Create the charge on Stripe's servers - this will charge the user's card 
-
-if request_command == 'member_payment':
-    token = request_params.getvalue ('stripeToken')
-    webuser_id = request_params.getvalue('webuser_id')
-    member_name = request_params.getvalue('webuser_name')
-    amount = request_params.getvalue('stripeDues')
-
-    with stripe_error_handling ():
-        charge = stripe.Charge.create( amount=amount,
-                                       currency="usd", 
-                                       card=token, 
-                                       description='Payment for member {0}'.format (member_name))
-
-    with stripe_refund_on_error (charge):
-#        assert charge.card.address_line1_check == 'pass', 'Address verification failed or unknown.'
-        assert charge.card.cvc_check == 'pass', 'CVC verification failed or unknown.'
-#        assert charge.card.address_zip_check == 'pass', 'Zipcode verification failed or unknown.'
-
-        balance = stripe.BalanceTransaction.retrieve (charge.balance_transaction)
-        conn =  psycopg2.connect ('dbname=hcoop_portal3test user=hcoop host=postgres port=5433')
-        cur = conn.cursor ()
-        cur.execute ('insert into stripe_payment (charge_id, card_name, webuser_id, paid_on, gross, fee) values (%s, %s, %s, %s, %s, %s)', 
-                     (charge.id, charge.card.name, webuser_id, datetime.date.today (), charge.amount, balance.fee))
-        conn.commit ()
-
-    notify_payment (charge, member_name)
-    stripe_success ('/portal/portal?cmd=stripeSuccess')
-elif request_command == 'reject_member_payment':
-    # todo: protect command using group file and separate cgi
-    charge_id = request_params.getvalue ('stripeChargeId');
-    reason = request_params.getvalue ('reason', 'none given');
-    with stripe_error_handling ():
-        conn = psycopg2.connect (connstring)
-        cur = conn.cursor ()
-
-        cur.execute ('SELECT charge_id FROM stripe_payment WHERE charge_id = %s', (charge_id,))
-        assert cur.fetchone() != None, 'Bad charge id'
-        cur.execute ('SELECT stripe_charge_id FROM stripe_handled WHERE stripe_charge_id = %s', (charge_id,))
-        assert cur.fetchone() == None, 'Cannot rejected a previously handled payment'
-        
-        charge = stripe.Charge.retrieve (charge_id);
-        charge.refund ()
-        cur.execute ('insert into stripe_rejected (stripe_charge_id, refunded_on, reason) values (%s, %s, %s)',
-                     (charge.id, datetime.date.today (), reason))
-        conn.commit ()
-
-    notify_payment_rejected (charge, reason)
-    stripe_success ('/portal/money?cmd=stripeRejected')
-else:
-    assert False, 'Invalid command.'
-
-# Use mod_authz_groupfile to store money/root
-#   (All hcoop members should be able to use this!)
-#  [support Satisfy? Satisfy: all is OK for now...]
-# Whenever groups are updated in the portal, write the file
-# make sure to store the file outside of the web root (duh)
-# only users in money/root can do reject/adduser
-# common code should go into a module (feh!)
-# application_payment in one cgi (anyone)
-# member_payment in another (only kerberos users)
-# reject_payment / capture_application_payment (kerberos + inGroup {money, root})
-
-# If there is a way to allow all and check the group info
-# here... maybe investigate, but beware security holes
-# alt: libapache2-mod-authnz-external + db helper script
-# can use ExternalGroup, check kerberos user is in group specified in
-# another env var
-
+#!/usr/bin/env python
+# -*- python -*-
+
+from hcoopstripe import *
+
+import stripe, cgi, psycopg2, cgitb, datetime, smtplib
+
+hcoop_stripe_init ()
+
+# Get the credit card details submitted by the form
+
+request_params = cgi.FieldStorage()
+request_command = request_params.getvalue ('cmd', 'none');
+
+assert request_command != 'none', 'No command given.'
+
+# Create the charge on Stripe's servers - this will charge the user's card 
+
+if request_command == 'member_payment':
+    token = request_params.getvalue ('stripeToken')
+    webuser_id = request_params.getvalue('webuser_id')
+    member_name = request_params.getvalue('webuser_name')
+    amount = request_params.getvalue('stripeDues')
+
+    with stripe_error_handling ():
+        charge = stripe.Charge.create( amount=amount,
+                                       currency="usd", 
+                                       card=token, 
+                                       description='Payment for member {0}'.format (member_name))
+
+    with stripe_refund_on_error (charge):
+#        assert charge.card.address_line1_check == 'pass', 'Address verification failed or unknown.'
+        assert charge.card.cvc_check == 'pass', 'CVC verification failed or unknown.'
+#        assert charge.card.address_zip_check == 'pass', 'Zipcode verification failed or unknown.'
+
+        balance = stripe.BalanceTransaction.retrieve (charge.balance_transaction)
+        conn =  psycopg2.connect ('dbname=hcoop_portal3test user=hcoop host=postgres port=5433')
+        cur = conn.cursor ()
+        cur.execute ('insert into stripe_payment (charge_id, card_name, webuser_id, paid_on, gross, fee) values (%s, %s, %s, %s, %s, %s)', 
+                     (charge.id, charge.card.name, webuser_id, datetime.date.today (), charge.amount, balance.fee))
+        conn.commit ()
+
+    notify_payment (charge, member_name)
+    stripe_success ('/portal/portal?cmd=stripeSuccess')
+else:
+    assert False, 'Invalid command.'
+
+# Use mod_authz_groupfile to store money/root
+#   (All hcoop members should be able to use this!)
+#  [support Satisfy? Satisfy: all is OK for now...]
+# Whenever groups are updated in the portal, write the file
+# make sure to store the file outside of the web root (duh)
+# only users in money/root can do reject/adduser
+# common code should go into a module (feh!)
+# application_payment in one cgi (anyone)
+# member_payment in another (only kerberos users)
+# reject_payment / capture_application_payment (kerberos + inGroup {money, root})
+
+# If there is a way to allow all and check the group info
+# here... maybe investigate, but beware security holes
+# alt: libapache2-mod-authnz-external + db helper script
+# can use ExternalGroup, check kerberos user is in group specified in
+# another env var
+