Repository: liberapay/liberapay.com

Issue:
Please expand information about reversed transactions
I have a payment set up by bank transfer with my IBAN number. It has been refused/reversed several times. My bank does not recognize that they have received a request for funds transfer. Is it possible to add more information to the notification so I can copy/paste it to my bank?


Discussion:
In the notification that was sent to you when the payment was initiated, you can find a link to the SEPA mandate that you "signed". That mandate contains information that your bank should be able to use to figure out what happened, including our SEPA creditor identifier and the mandate's identifier.

Resolving patch:
diff --git a/liberapay/models/exchange_route.py b/liberapay/models/exchange_route.py
--- a/liberapay/models/exchange_route.py
+++ b/liberapay/models/exchange_route.py
@@ -326,6 +326,16 @@ def get_partial_number(self):
         else:
             raise NotImplementedError(self.network)
 
+    def has_been_charged_successfully(self):
+        return bool(self.db.one("""
+            SELECT 1
+              FROM payins pi
+             WHERE pi.payer = %s
+               AND pi.route = %s
+               AND pi.status = 'succeeded'
+             LIMIT 1
+        """, (self.participant.id, self.id)))
+
     @cached_property
     def stripe_payment_method(self):
         return stripe.PaymentMethod.retrieve(self.address)


Test patch:
diff --git a/tests/py/fixtures/TestPayinsStripe.yml b/tests/py/fixtures/TestPayinsStripe.yml
--- a/tests/py/fixtures/TestPayinsStripe.yml
+++ b/tests/py/fixtures/TestPayinsStripe.yml
@@ -2557,4 +2557,140 @@ interactions:
       request-id: [req_ITW5PZ9zrONknE]
       stripe-version: ['2019-08-14']
     status: {code: 200, message: OK}
+- request:
+    body: null
+    headers: {}
+    method: GET
+    uri: https://api.stripe.com/v1/sources/src_1GVfmUFk4eGpfLOCe5TNONii
+  response:
+    body: {string: "{\n  \"id\": \"src_1GVfmUFk4eGpfLOCe5TNONii\",\n  \"object\":\
+        \ \"source\",\n  \"amount\": 5200,\n  \"client_secret\": \"src_client_secret_kj23fN32n4i3Go8ii9AcaCOB\"\
+        ,\n  \"created\": 1586359534,\n  \"currency\": \"eur\",\n  \"customer\": \"\
+        cus_H3nNPCdpWqqLZR\",\n  \"flow\": \"none\",\n  \"livemode\": false,\n  \"\
+        mandate\": {\n    \"acceptance\": {\n      \"date\": null,\n      \"ip\":\
+        \ null,\n      \"offline\": null,\n      \"online\": null,\n      \"status\"\
+        : \"pending\",\n      \"type\": null,\n      \"user_agent\": null\n    },\n\
+        \    \"amount\": null,\n    \"currency\": null,\n    \"interval\": \"variable\"\
+        ,\n    \"notification_method\": \"none\",\n    \"reference\": \"GQRBWRPGVJAPHAQG\"\
+        ,\n    \"url\": \"https://hooks.stripe.com/adapter/sepa_debit/file/src_1GVfmUFk4eGpfLOCe5TNONii/src_client_secret_kj23fN32n4i3Go8ii9AcaCOB\"\
+        \n  },\n  \"metadata\": {\n  },\n  \"owner\": {\n    \"address\": {\n    \
+        \  \"city\": null,\n      \"country\": \"DE\",\n      \"line1\": null,\n \
+        \     \"line2\": null,\n      \"postal_code\": null,\n      \"state\": null\n\
+        \    },\n    \"email\": \"donor@example.com\",\n    \"name\": \"Jane Doe\"\
+        ,\n    \"phone\": null,\n    \"verified_address\": null,\n    \"verified_email\"\
+        : null,\n    \"verified_name\": null,\n    \"verified_phone\": null\n  },\n\
+        \  \"sepa_debit\": {\n    \"last4\": \"3000\",\n    \"bank_code\": \"37040044\"\
+        ,\n    \"fingerprint\": \"VNz5BVW8XnYcq3XT\",\n    \"country\": \"DE\",\n\
+        \    \"mandate_reference\": \"GQRBWRPGVJAPHAQG\",\n    \"mandate_url\": \"\
+        https://hooks.stripe.com/adapter/sepa_debit/file/src_1GVfmUFk4eGpfLOCe5TNONii/src_client_secret_kj23fN32n4i3Go8ii9AcaCOB\"\
+        ,\n    \"branch_code\": null\n  },\n  \"statement_descriptor\": null,\n  \"\
+        status\": \"chargeable\",\n  \"type\": \"sepa_debit\",\n  \"usage\": \"reusable\"\
+        \n}\n"}
+    headers:
+      Connection: [keep-alive]
+      Content-Length: ['1581']
+      Content-Type: [application/json]
+      Date: ['Fri, 01 Jan 2021 09:28:38 GMT']
+      Server: [nginx]
+      Strict-Transport-Security: [max-age=31556926; includeSubDomains; preload]
+      access-control-allow-credentials: ['true']
+      access-control-allow-methods: ['GET, POST, HEAD, OPTIONS, DELETE']
+      access-control-allow-origin: ['*']
+      access-control-expose-headers: ['Request-Id, Stripe-Manage-Version, X-Stripe-External-Auth-Required,
+          X-Stripe-Privileged-Session-Required']
+      access-control-max-age: ['300']
+      cache-control: ['no-cache, no-store']
+      request-id: [req_t4q1zcqlCNMGyw]
+      stripe-version: ['2019-08-14']
+    status: {code: 200, message: OK}
+- request:
+    body: null
+    headers: {}
+    method: GET
+    uri: https://api.stripe.com/v1/sources/src_1GVfmaFk4eGpfLOCvkI01RbI
+  response:
+    body: {string: "{\n  \"id\": \"src_1GVfmaFk4eGpfLOCvkI01RbI\",\n  \"object\":\
+        \ \"source\",\n  \"amount\": null,\n  \"client_secret\": \"src_client_secret_htLpl1mft7YxN3JS5kEfi84A\"\
+        ,\n  \"created\": 1586359540,\n  \"currency\": \"eur\",\n  \"customer\": \"\
+        cus_H3nNLyErBliltd\",\n  \"flow\": \"none\",\n  \"livemode\": false,\n  \"\
+        mandate\": {\n    \"acceptance\": {\n      \"date\": null,\n      \"ip\":\
+        \ null,\n      \"offline\": null,\n      \"online\": null,\n      \"status\"\
+        : \"pending\",\n      \"type\": null,\n      \"user_agent\": null\n    },\n\
+        \    \"amount\": null,\n    \"currency\": null,\n    \"interval\": \"variable\"\
+        ,\n    \"notification_method\": \"none\",\n    \"reference\": \"WXNWNGBR4FQMBW7I\"\
+        ,\n    \"url\": \"https://hooks.stripe.com/adapter/sepa_debit/file/src_1GVfmaFk4eGpfLOCvkI01RbI/src_client_secret_htLpl1mft7YxN3JS5kEfi84A\"\
+        \n  },\n  \"metadata\": {\n  },\n  \"owner\": {\n    \"address\": {\n    \
+        \  \"city\": null,\n      \"country\": \"FR\",\n      \"line1\": null,\n \
+        \     \"line2\": null,\n      \"postal_code\": null,\n      \"state\": null\n\
+        \    },\n    \"email\": \"donor@example.com\",\n    \"name\": \"Jane Doe\"\
+        ,\n    \"phone\": null,\n    \"verified_address\": null,\n    \"verified_email\"\
+        : null,\n    \"verified_name\": null,\n    \"verified_phone\": null\n  },\n\
+        \  \"sepa_debit\": {\n    \"last4\": \"2606\",\n    \"bank_code\": \"20041\"\
+        ,\n    \"branch_code\": \"01005\",\n    \"fingerprint\": \"bevtMDy4Cud4ZswV\"\
+        ,\n    \"country\": \"FR\",\n    \"mandate_reference\": \"WXNWNGBR4FQMBW7I\"\
+        ,\n    \"mandate_url\": \"https://hooks.stripe.com/adapter/sepa_debit/file/src_1GVfmaFk4eGpfLOCvkI01RbI/src_client_secret_htLpl1mft7YxN3JS5kEfi84A\"\
+        \n  },\n  \"statement_descriptor\": null,\n  \"status\": \"chargeable\",\n\
+        \  \"type\": \"sepa_debit\",\n  \"usage\": \"reusable\"\n}\n"}
+    headers:
+      Connection: [keep-alive]
+      Content-Length: ['1581']
+      Content-Type: [application/json]
+      Date: ['Fri, 01 Jan 2021 09:28:39 GMT']
+      Server: [nginx]
+      Strict-Transport-Security: [max-age=31556926; includeSubDomains; preload]
+      access-control-allow-credentials: ['true']
+      access-control-allow-methods: ['GET, POST, HEAD, OPTIONS, DELETE']
+      access-control-allow-origin: ['*']
+      access-control-expose-headers: ['Request-Id, Stripe-Manage-Version, X-Stripe-External-Auth-Required,
+          X-Stripe-Privileged-Session-Required']
+      access-control-max-age: ['300']
+      cache-control: ['no-cache, no-store']
+      request-id: [req_z2PHXL8RslSo5S]
+      stripe-version: ['2019-08-14']
+    status: {code: 200, message: OK}
+- request:
+    body: null
+    headers: {}
+    method: GET
+    uri: https://api.stripe.com/v1/sources/src_1GwWqpFk4eGpfLOC099akWU5
+  response:
+    body: {string: "{\n  \"id\": \"src_1GwWqpFk4eGpfLOC099akWU5\",\n  \"object\":\
+        \ \"source\",\n  \"amount\": null,\n  \"client_secret\": \"src_client_secret_M9YbmMG9mBRivE1ff3iA4E2Y\"\
+        ,\n  \"created\": 1592760063,\n  \"currency\": \"eur\",\n  \"customer\": \"\
+        cus_HVXw26RUl9t8v8\",\n  \"flow\": \"none\",\n  \"livemode\": false,\n  \"\
+        mandate\": {\n    \"acceptance\": {\n      \"date\": null,\n      \"ip\":\
+        \ null,\n      \"offline\": null,\n      \"online\": null,\n      \"status\"\
+        : \"pending\",\n      \"type\": null,\n      \"user_agent\": null\n    },\n\
+        \    \"amount\": null,\n    \"currency\": null,\n    \"interval\": \"variable\"\
+        ,\n    \"notification_method\": \"none\",\n    \"reference\": \"W9MCQ0MHJ2VEZLGR\"\
+        ,\n    \"url\": \"https://hooks.stripe.com/adapter/sepa_debit/file/src_1GwWqpFk4eGpfLOC099akWU5/src_client_secret_M9YbmMG9mBRivE1ff3iA4E2Y\"\
+        \n  },\n  \"metadata\": {\n  },\n  \"owner\": {\n    \"address\": {\n    \
+        \  \"city\": null,\n      \"country\": \"FR\",\n      \"line1\": null,\n \
+        \     \"line2\": null,\n      \"postal_code\": null,\n      \"state\": null\n\
+        \    },\n    \"email\": \"donor@example.com\",\n    \"name\": \"Jane Doe\"\
+        ,\n    \"phone\": null,\n    \"verified_address\": null,\n    \"verified_email\"\
+        : null,\n    \"verified_name\": null,\n    \"verified_phone\": null\n  },\n\
+        \  \"sepa_debit\": {\n    \"last4\": \"2606\",\n    \"bank_code\": \"20041\"\
+        ,\n    \"branch_code\": \"01005\",\n    \"fingerprint\": \"bevtMDy4Cud4ZswV\"\
+        ,\n    \"country\": \"FR\",\n    \"mandate_reference\": \"W9MCQ0MHJ2VEZLGR\"\
+        ,\n    \"mandate_url\": \"https://hooks.stripe.com/adapter/sepa_debit/file/src_1GwWqpFk4eGpfLOC099akWU5/src_client_secret_M9YbmMG9mBRivE1ff3iA4E2Y\"\
+        \n  },\n  \"statement_descriptor\": null,\n  \"status\": \"chargeable\",\n\
+        \  \"type\": \"sepa_debit\",\n  \"usage\": \"reusable\"\n}\n"}
+    headers:
+      Connection: [keep-alive]
+      Content-Length: ['1581']
+      Content-Type: [application/json]
+      Date: ['Fri, 01 Jan 2021 09:28:41 GMT']
+      Server: [nginx]
+      Strict-Transport-Security: [max-age=31556926; includeSubDomains; preload]
+      access-control-allow-credentials: ['true']
+      access-control-allow-methods: ['GET, POST, HEAD, OPTIONS, DELETE']
+      access-control-allow-origin: ['*']
+      access-control-expose-headers: ['Request-Id, Stripe-Manage-Version, X-Stripe-External-Auth-Required,
+          X-Stripe-Privileged-Session-Required']
+      access-control-max-age: ['300']
+      cache-control: ['no-cache, no-store']
+      request-id: [req_ySTp9dEVqAYZen]
+      stripe-version: ['2019-08-14']
+    status: {code: 200, message: OK}
 version: 1