X-Git-Url: http://git.hcoop.net/hcoop/portal.git/blobdiff_plain/f8b39e09ad3ca69efbb791352f72d8aa98c4cd34..bd5d2441217811dbf5c1f8be9faad1be3a64ac49:/stripe/stripe-payment.cgi diff --git a/stripe/stripe-payment.cgi b/stripe/stripe-payment.cgi index 130bac2..a6cd3e4 100755 --- a/stripe/stripe-payment.cgi +++ b/stripe/stripe-payment.cgi @@ -6,6 +6,7 @@ sys.path.insert(0, '/afs/hcoop.net/user/h/hc/hcoop/portal3/stripe/stripe-pkg/lib import stripe, cgi, psycopg2, cgitb, datetime, smtplib from email.mime.text import MIMEText +from contextlib import contextmanager cgitb.enable() @@ -28,66 +29,157 @@ page at your earliest convenience to process the payment. 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 '' + print 'Transaction Failed' + print '' + print '

Failed

Reason: ' + print e.json_body['error']['message'] + print '

' + print '' + print '' + raise + except stripe.error.StripeError, e: # General stripe failure + print 'Status: 200 OK' + print + print '' + print 'Stripe Error' + print '' + print '

Failed

Reason: ' + print e.json_body['error']['message'] + print '

' + print '' + print '' + raise + +@contextmanager +def stripe_refund_on_error (charge): + try: + yield + except: + print 'Status: 200 OK' + print 'Content-Type: text/html' + print '' + print '

Something went wrong after accepting payment!

' + charge.refund () + print '

The charge should be refunded. Please contact payment@hcoop.net if it was not!

' + raise + +def stripe_success (redirect_to): + print 'Status: 303 See Other' + print 'Location: {0}'.format(redirect_to); + print '' + print 'Go back to the portal'.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'); -token = request_params.getvalue ('stripeToken') -webuser_id = request_params.getvalue('webuser_id') -member_name = request_params.getvalue('webuser_name') -amount = request_params.getvalue('stripeDues') +assert request_command != 'none', 'No command given.' # Create the charge on Stripe's servers - this will charge the user's card -try: - charge = stripe.Charge.create( amount=amount, - currency="usd", - card=token, - description='Payment for member {0}'.format (member_name)) -except stripe.error.CardError, e: # The card has been declined - print 'Status: 200 OK' - print - print '' - print 'Transaction Failed' - print '' - print '

Failed

Reason: ' - print e.json_body['error']['message'] - print '

' - print '' - print '' -else: - try: - balance = stripe.BalanceTransaction.retrieve (charge.balance_transaction); - conn = psycopg2.connect ('dbname=hcoop_portal3test user=hcoop host=postgres port=5433') +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)', + 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)) - except: - print 'Status: 200 OK' - print 'Content-Type: text/html' - print '' - print '

Something went wrong after accepting payment!

' + 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 () - conn.rollback () - print '

The charge should be refunded. Please contact payment@hcoop.net if it was not!

' - raise - else: + cur.execute ('insert into stripe_rejected (stripe_charge_id, refunded_on, reason) values (%s, %s, %s)', + (charge.id, datetime.date.today (), reason)) conn.commit () - cur.close () - conn.close () - notify_payment (charge, member_name) - print 'Status: 303 See Other' - print 'Location: /portal/portal?cmd=stripeSuccess' - print '' - print 'Go back to the portal' + + 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 +