Repository: gratipay/gratipay.com

Issue:
keep track of credit card failures in the database
Credit card failures are logged during payday, but those logs are in flat files, and we don't have it in the db when someone's credit card fails. It would be great to use that info as part of #355.

<bountysource-plugin>

---

Want to back this issue? **[Place a bounty on it!](https://www.bountysource.com/issues/316897-keep-track-of-credit-card-failures-in-the-database?utm_campaign=plugin&utm_content=tracker%2F85909&utm_medium=issues&utm_source=github)** We accept bounties via [Bountysource](https://www.bountysource.com/?utm_campaign=plugin&utm_content=tracker%2F85909&utm_medium=issues&utm_source=github).
</bountysource-plugin>



Discussion:
cc @roguelynn

Who is your payment processor and how do you monitor incoming payments?

@RoarMax @balanced is. I'm not sure how he monitors incoming payments though.

@RoarMax You look really shady, because your GitHub account was created yesterday, and is completely empty. Would you care to attempt to convince us that you're not @delpan?

I mean, you may very well be someone I heard from by email, and if so I appreciate you jumping in here. You can understand how it looks from over here, though, right?

Fine fuck it so, god forbid trying to help. yes I'm the master criminal behind delpan, not a fraud professional trying to help. 

Woah @RoarMax , calm down please. I can only assume you're [RoarMax from twitter](https://twitter.com/RoarMax) but there's no way to identify who you are. No name, no email address, no blog/website, nothing. I can not blame @whit537 for being paranoid especially since I asked him if he knew who you were. For a fraud professional you really don't seem to take well to skepticism. :-P

:disappointed:

Sorry, @RoarMax.

Closing in favor of #1549.

This is a different issue than #1549.


Resolving patch:
diff --git a/gratipay/billing/exchanges.py b/gratipay/billing/exchanges.py
--- a/gratipay/billing/exchanges.py
+++ b/gratipay/billing/exchanges.py
@@ -194,7 +194,7 @@ def create_card_hold(db, participant, amount):
     except Exception as e:
         error = repr_exception(e)
         log(msg + "failed: %s" % error)
-        propagate_exchange(db, participant, 'bill', error, 0)
+        record_exchange(db, 'bill', amount, fee, participant, 'failed', error)
 
     return hold, error
 
@@ -264,7 +264,7 @@ def _prep_hit(unrounded):
     return cents, amount_str, upcharged, fee
 
 
-def record_exchange(db, kind, amount, fee, participant, status):
+def record_exchange(db, kind, amount, fee, participant, status, error=None):
     """Given a Bunch of Stuff, return None.
 
     Records in the exchanges table have these characteristics:
@@ -287,7 +287,9 @@ def record_exchange(db, kind, amount, fee, participant, status):
          RETURNING id
         """, (amount, fee, participant.username, status))
 
-        if amount < 0:
+        if status == 'failed':
+            propagate_exchange(cursor, participant, kind, error, 0)
+        elif amount < 0:
             amount -= fee
             propagate_exchange(cursor, participant, kind, '', amount)
 


Test patch:
diff --git a/tests/py/test_billing_exchanges.py b/tests/py/test_billing_exchanges.py
--- a/tests/py/test_billing_exchanges.py
+++ b/tests/py/test_billing_exchanges.py
@@ -82,6 +82,13 @@ def test_create_card_hold_failure(self, Customer):
         hold, error = create_card_hold(self.db, self.janet, D('1.00'))
         assert hold is None
         assert error == "Foobar()"
+        exchange = self.db.one("SELECT * FROM exchanges")
+        assert exchange
+        assert exchange.amount
+        assert exchange.status == 'failed'
+        janet = Participant.from_id(self.janet.id)
+        assert self.janet.last_bill_result == 'Foobar()'
+        assert self.janet.balance == janet.balance == 0
 
     def test_create_card_hold_success(self):
         hold, error = create_card_hold(self.db, self.janet, D('1.00'))