Adamw has uploaded a new change for review.
https://gerrit.wikimedia.org/r/49369
Change subject: audit paypal payments as well
......................................................................
audit paypal payments as well
Change-Id: I29dd646f4d9c15e2791496f969a091fa2f4490cd
---
A audit/paypal/civicrm.py
M audit/paypal/history.py
M audit/paypal/paypal-audit.cfg
3 files changed, 130 insertions(+), 35 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/wikimedia/fundraising/tools
refs/changes/69/49369/1
diff --git a/audit/paypal/civicrm.py b/audit/paypal/civicrm.py
new file mode 100644
index 0000000..347d934
--- /dev/null
+++ b/audit/paypal/civicrm.py
@@ -0,0 +1,35 @@
+import MySQLdb as Dbi
+
+db_conn = False
+
+class Civicrm(object):
+ def __init__(self, config):
+ self.db = Connection(**dict(config.items("Db")))
+ self.config = config
+
+ def transaction_exists(self, gateway_txn_id, gateway="paypal"):
+ sql = """
+SELECT COUNT(*) AS count FROM wmf_contribution_extra
+ WHERE gateway = %s AND gateway_txn_id = %s
+ """
+
+ count = list(self.db.execute(sql, (gateway, gateway_txn_id)))
+ return count[0]['count'] > 0
+
+class Connection(object):
+ def __init__(self, host=None, user=None, passwd=None, db=None,
debug=False):
+ self.db_conn = Dbi.connect(host=host, user=user, passwd=passwd, db=db)
+ self.debug = debug.lower() in ("true", "1")
+
+ def close(self):
+ #self.db_conn.commit()
+ pass
+
+ def execute(self, sql, params=None):
+ cursor = self.db_conn.cursor(cursorclass=Dbi.cursors.DictCursor)
+ if self.debug:
+ print sql, params
+ cursor.execute(str(sql), params)
+ for row in cursor.fetchall():
+ yield row
+ cursor.close()
diff --git a/audit/paypal/history.py b/audit/paypal/history.py
index cb0685f..d241db9 100755
--- a/audit/paypal/history.py
+++ b/audit/paypal/history.py
@@ -11,9 +11,10 @@
import gzip
import locale
import dateutil.parser
+import civicrm
def main():
- global config, messaging, options
+ global config, messaging, options, civi
parser = OptionParser(usage="usage: %prog [options]")
parser.add_option("-c", "--config", dest='configFile', default=[
"paypal-audit.cfg" ], action='append', help='Path to configuration file')
parser.add_option("-f", "--auditFile", dest='auditFile', default=None,
help='CSV of transaction history')
@@ -35,20 +36,12 @@
log("*** Dummy mode! Not injecting stomp messages ***")
messaging = Stomp(config)
+ civi = civicrm.Civicrm(config)
locale.setlocale(locale.LC_NUMERIC, "")
# fix spurious whitespace around column header names
infile.fieldnames = [ name.strip() for name in infile.fieldnames ]
-
- ipn_handled_types = [
- "Subscription Payment Received",
- "Web Accept Payment Received",
- "Shopping Cart Payment Received",
- "Virtual Debt Card Credit Received",
- "Payment Received",
- "Update to eCheck Received",
- ]
ignore_types = [
"Authorization",
@@ -66,10 +59,17 @@
"Reversal": handle_refund,
"Chargeback Settlement": handle_refund,
"Refund": handle_refund,
+
+ "Subscription Payment Received": handle_payment,
+ "Web Accept Payment Received": handle_payment,
+ "Shopping Cart Payment Received": handle_payment,
+ "Virtual Debt Card Credit Received": handle_payment,
+ "Payment Received": handle_payment,
+ "Update to eCheck Received": handle_payment,
}
for line in infile:
- if line['Type'] in ipn_handled_types + ignore_types:
+ if line['Type'] in ignore_types:
log("Ignoring %s of type %s" % (line['Transaction ID'],
line['Type']))
continue
if line['Type'] in audit_dispatch:
@@ -81,7 +81,7 @@
log("Unhandled transaction, type \"%s\": %s" % (line['Type'],
json.dumps(line)))
def handle_refund(line):
- global config, messaging
+ global config, messaging, civi
if line['Status'] != "Completed":
return handle_unknown(line)
@@ -89,17 +89,66 @@
txn_id = line['Transaction ID']
# Construct the STOMP message
- headers = {
- 'correlation-id': 'paypal-%s' % txn_id,
- 'destination': config.get('Stomp', 'refund-queue'),
- 'persistent': 'true',
- }
+ msg = normalize_refund_msg(line)
+
+ if not civi.transaction_exists(txn_id):
+ log("Queueing refund %s" % (txn_id, ))
+ messaging.send(msg, "refund")
+ else:
+ log("Refund already exists: %s" % (txn_id, ))
+
+def handle_payment(line):
+ global config, messaging, civi
+
+ if line['Status'] != "Completed":
+ return handle_unknown(line)
+
+ txn_id = line['Transaction ID']
+
+ # Construct the STOMP message
msg = normalize_msg(line)
- log("Queueing refund %s" % (txn_id, ))
- messaging.send(headers, msg)
+ if not civi.transaction_exists(txn_id):
+ log("Queueing payment %s" % (txn_id, ))
+ messaging.send(msg, "payment")
+ else:
+ log("Payment already exists: %s" % (txn_id, ))
def normalize_msg(line):
+ timestamp = dateutil.parser.parse(
+ line['Date'] + " " + line['Time'] + " " + line['Time Zone'],
+ ).strftime("%s")
+
+ names = line['Name'].split(" ")
+
+ return {
+ 'date': timestamp,
+ 'email': line['From Email Address'],
+
+ 'first_name': names[0],
+ 'last_name': " ".join(names[1:]),
+
+ 'street_address': line['Address Line 1'],
+ 'supplemental_address_1': line['Address Line 2/District/Neighborhood'],
+ 'city': line['Town/City'],
+ 'state_province':
line['State/Province/Region/County/Territory/Prefecture/Republic'],
+ 'country': line['Country'],
+ 'postal_code': line['Zip/Postal Code'],
+
+ 'comment': line['Note'],
+ # somthing with: line['Subscription Number'],
+
+ 'gross_currency': line['Currency'],
+ 'gross': round(locale.atof(line['Gross']), 2),
+ 'fee': round(locale.atof(line['Fee']), 2),
+ 'net': round(locale.atof(line['Net']), 2),
+ 'gateway': "paypal",
+ 'gateway_txn_id': line['Transaction ID'],
+ }
+
+def normalize_refund_msg(line):
+ msg = normalize_msg(line)
+
refund_type = "unknown"
if line['Type'] == "Refund":
refund_type = "refund"
@@ -108,23 +157,16 @@
elif line['Type'] == "Reversal":
refund_type = "reversal"
- timestamp = dateutil.parser.parse(
- line['Date'] + " " + line['Time'] + " " + line['Time Zone'],
- ).strftime("%s")
-
- return {
- 'date': timestamp,
- 'email': line['From Email Address'],
- 'gross_currency': line['Currency'],
- 'gross': round(0 - locale.atof(line['Gross']), 2),
- 'fee': round(0 - locale.atof(line['Fee']), 2),
- 'net': round(0 - locale.atof(line['Net']), 2),
+ msg.update({
+ 'gross': 0 - msg['gross'],
+ 'fee': 0 - msg['fee'],
+ 'net': 0 - msg['net'],
+ 'type': refund_type,
'gateway_refund_id': line['Transaction ID'],
'gateway_parent_id': line['Reference Txn ID'],
- 'gateway': "paypal",
- 'type': refund_type,
- }
+ })
+ return msg
class Stomp(object):
def __init__(self, config):
@@ -141,13 +183,22 @@
import time
time.sleep(1)
- def send(self, headers=None, msg=None):
- global options
+ def send(self, msg, queue_name):
+ global options, config
if options.noEffect:
log("not queueing message. " + json.dumps(msg))
return
+ headers = {
+ 'correlation-id': '%s-%s' % (msg['gateway'],
msg['gateway_txn_id']),
+ 'destination': config.get('Stomp', '%s-queue' % (queue_name,)),
+ 'persistent': 'true',
+ }
+
+ if config.getboolean('Stomp', 'debug'):
+ log("sending %s %s" % (headers, msg))
+
self.sc.send(
json.dumps(msg),
headers
diff --git a/audit/paypal/paypal-audit.cfg b/audit/paypal/paypal-audit.cfg
index 291b4f9..0782393 100644
--- a/audit/paypal/paypal-audit.cfg
+++ b/audit/paypal/paypal-audit.cfg
@@ -2,3 +2,12 @@
server: localhost
port: 61613
refund-queue: /queue/refund-notifications
+payment-queue: /queue/donations
+debug: false
+
+[Db]
+host: localhost
+user: USER
+passwd: PASS
+db: DB
+debug: false
--
To view, visit https://gerrit.wikimedia.org/r/49369
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I29dd646f4d9c15e2791496f969a091fa2f4490cd
Gerrit-PatchSet: 1
Gerrit-Project: wikimedia/fundraising/tools
Gerrit-Branch: master
Gerrit-Owner: Adamw <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits