Created
February 20, 2012 15:39
-
-
Save doobeh/1869698 to your computer and use it in GitHub Desktop.
PayPal Flask IPN Engine
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- coding: utf-8 -*- | |
""" | |
IPN Engine | |
~~~~~~~~~~ | |
PayPal's Instant Payment Notification (IPN) helps integrate PayPal more | |
deeply into your web application. When activated, PayPal will send a | |
POST request to a specified URL when a transaction's status changes. | |
The most obvious use is to trigger a state change, enable an account, | |
download, or trigger an email when a payment is made. | |
To validate PayPals POST data, the data must be sent back to PayPal | |
in exactly the same order, prefixed with 'cmd=_notify-validate'. If | |
valid PayPal responds with 'VERIFIED' if not it returns 'INVALID'. | |
Requires: flask, requests | |
""" | |
from flask import Flask, request | |
import requests | |
from werkzeug.datastructures import ImmutableOrderedMultiDict | |
app = Flask(__name__) | |
@app.route('/ipn',methods=['POST']) | |
def ipn(): | |
arg = '' | |
#: We use an ImmutableOrderedMultiDict item because it retains the order. | |
request.parameter_storage_class = ImmutableOrderedMultiDict() | |
values = request.form | |
for x, y in values.iteritems(): | |
arg += "&{x}={y}".format(x=x,y=y) | |
validate_url = 'https://www.sandbox.paypal.com' \ | |
'/cgi-bin/webscr?cmd=_notify-validate{arg}' \ | |
.format(arg=arg) | |
print 'Validating IPN using {url}'.format(url=validate_url) | |
r = requests.get(validate_url) | |
if r.text == 'VERIFIED': | |
print "PayPal transaction was verified successfully." | |
# Do something with the verified transaction details. | |
payer_email = request.form.get('payer_email') | |
print "Pulled {email} from transaction".format(email=payer_email) | |
else: | |
print 'Paypal IPN string {arg} did not validate'.format(arg=arg) | |
return r.text | |
if __name__ == '__main__': | |
app.debug = True | |
app.run("127.0.0.1",5000) |
Nevermind. It works. Apparently the Flask Test Client ignores parameter_storage_class
, so you can't preserve the parameter ordering in tests.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Do you know if this works? I'm trying something similar, but the parameters seem to get out of order somehow.