Skip to content
Permalink
Browse files

[FIX] payment_stripe_sca: better error handling

In some cases, Stripe will return the result of a transaction with
a faulty HTTP Status (e.g. 4XX statuses) - not because the request
was malformed, but because the payment failed. It is a bit unfortunate
that Stripe would not differentiate between payment status and request
validity, but that's the state of things.

This commit ensures that such a response will be logged correctly, by
making the call to `raise_for_status` conditionnal on the response object's
status code and internal structure, forwarding the response's json to the
validation flow if the status is in the 4XX range and a `code` key is
found in the response, as described in https://stripe.com/docs/error-codes

While testing this fix, it became apparent that some error message
processing in the frontend was not correctly handled as well - instead
of purely rejecting the promise, a failed payment should still send its
payload to the backend, to allow the server to put the transaction in
the correct state according to the response.
  • Loading branch information
dbo-odoo committed Dec 2, 2019
1 parent 11d8fd4 commit 64551acea2655209127e365737cd76085e4cf5ad
@@ -53,13 +53,19 @@ def _stripe_request(self, url, data=False, method="POST"):
"Stripe-Version": "2019-05-16", # SetupIntent need a specific version
}
resp = requests.request(method, url, data=data, headers=headers)
try:
resp.raise_for_status()
except HTTPError:
_logger.error(resp.text)
stripe_error = resp.json().get('error', {}).get('message', '')
error_msg = " " + (_("Stripe gave us the following info about the problem: '%s'") % stripe_error)
raise ValidationError(error_msg)
# Stripe can send 4XX errors for payment failure (not badly-formed requests)
# check if error `code` is present in 4XX response and raise only if not
# cfr https://stripe.com/docs/error-codes
# these can be made customer-facing, as they usually indicate a problem with the payment
# (e.g. insufficient funds, expired card, etc.)
if not resp.ok and not (400 <= resp.status_code < 500 and resp.json().get('error', {}).get('code')):
try:
resp.raise_for_status()
except HTTPError:
_logger.error(resp.text)
stripe_error = resp.json().get('error', {}).get('message', '')
error_msg = " " + (_("Stripe gave us the following info about the problem: '%s'") % stripe_error)
raise ValidationError(error_msg)
return resp.json()

def _create_stripe_session(self, kwargs):
@@ -310,11 +316,8 @@ def _stripe_s2s_validate_tree(self, tree):
self._set_transaction_pending()
return True
else:
error = tree.get("failure_message") or tree['error']['message']
_logger.warning(error)
vals.update({"state_message": error})
self.write(vals)
self._set_transaction_cancel()
error = tree.get("failure_message") or tree.get('error', {}).get('message')
self._set_transaction_error(error)
return False

def _stripe_form_get_invalid_parameters(self, data):
@@ -21,12 +21,9 @@ odoo.define('payment_stripe_sca.processing', function (require) {
stripe.handleCardPayment(tx.stripe_payment_intent_secret)
.then(function(result) {defer.resolve(result)})
}).then(function(result) {
if (result.error) {
return $.Deferred().reject({"message": {"data": { "message": result.error.message}}});
}
return rpc.query({
route: '/payment/stripe/s2s/process_payment_intent',
params: _.extend({}, result.paymentIntent, {reference: tx.reference}),
params: _.extend({}, result.paymentIntent, {reference: tx.reference, error: result.error}),
});
}).then(function(result) {
window.location = '/payment/process';

0 comments on commit 64551ac

Please sign in to comment.
You can’t perform that action at this time.