summaryrefslogtreecommitdiff
path: root/addons/payment_stripe/models/payment.py
diff options
context:
space:
mode:
Diffstat (limited to 'addons/payment_stripe/models/payment.py')
-rw-r--r--addons/payment_stripe/models/payment.py78
1 files changed, 71 insertions, 7 deletions
diff --git a/addons/payment_stripe/models/payment.py b/addons/payment_stripe/models/payment.py
index 4e68d122bc1..ce9ff886777 100644
--- a/addons/payment_stripe/models/payment.py
+++ b/addons/payment_stripe/models/payment.py
@@ -3,6 +3,7 @@
import logging
import requests
import pprint
+from requests.exceptions import HTTPError
from werkzeug import urls
from odoo import api, fields, models, _
@@ -35,7 +36,7 @@ class PaymentAcquirerStripe(models.Model):
def stripe_form_generate_values(self, tx_values):
self.ensure_one()
- base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
+ base_url = self.get_base_url()
stripe_session_data = {
'payment_method_types[]': 'card',
'line_items[][amount]': int(tx_values['amount'] if tx_values['currency'].name in INT_CURRENCIES else float_round(tx_values['amount'] * 100, 2)),
@@ -61,8 +62,12 @@ class PaymentAcquirerStripe(models.Model):
resp = requests.request(method, url, data=data, headers=headers)
try:
resp.raise_for_status()
- except:
- _logger.error(resp.text)
+ except HTTPError:
+ _logger.error("Here are some more information for the "
+ "following HTTP error on %s:\n"
+ "Request data:\n%s\n"
+ "Response body:\n%s",
+ url, pprint.pformat(data), resp.text)
raise
return resp.json()
@@ -127,6 +132,19 @@ class PaymentTransactionStripe(models.Model):
_inherit = 'payment.transaction'
stripe_payment_intent = fields.Char(string='Stripe Payment Intent ID', readonly=True)
+ stripe_payment_intent_secret = fields.Char(string='Stripe Payment Intent Secret', readonly=True)
+
+
+ def _get_processing_info(self):
+ res = super()._get_processing_info()
+ if self.acquirer_id.provider == 'stripe':
+ stripe_info = {
+ 'stripe_payment_intent': self.stripe_payment_intent,
+ 'stripe_payment_intent_secret': self.stripe_payment_intent_secret,
+ 'stripe_publishable_key': self.acquirer_id.stripe_publishable_key,
+ }
+ res.update(stripe_info)
+ return res
def form_feedback(self, data, acquirer_name):
if data.get('reference') and acquirer_name == 'stripe':
@@ -142,14 +160,21 @@ class PaymentTransactionStripe(models.Model):
return super(PaymentTransactionStripe, self).form_feedback(data, acquirer_name)
def _stripe_create_payment_intent(self, acquirer_ref=None, email=None):
+ if not self.payment_token_id.stripe_payment_method:
+ # old token before using sca, need to fetch data from the api
+ self.payment_token_id._stripe_sca_migrate_customer()
+
charge_params = {
'amount': int(self.amount if self.currency_id.name in INT_CURRENCIES else float_round(self.amount * 100, 2)),
'currency': self.currency_id.name.lower(),
- 'setup_future_usage': 'off_session',
+ 'off_session': True,
'confirm': True,
'payment_method': self.payment_token_id.stripe_payment_method,
'customer': self.payment_token_id.acquirer_ref,
+ "description": self.reference,
}
+ if not self.env.context.get('off_session'):
+ charge_params.update(setup_future_usage='off_session', off_session=False)
_logger.info('_stripe_create_payment_intent: Sending values to stripe, values:\n%s', pprint.pformat(charge_params))
res = self.acquirer_id._stripe_request('payment_intents', charge_params)
@@ -213,15 +238,18 @@ class PaymentTransactionStripe(models.Model):
def _stripe_s2s_validate_tree(self, tree):
self.ensure_one()
- if self.state != 'draft':
+ if self.state not in ("draft", "pending"):
_logger.info('Stripe: trying to validate an already validated tx (ref %s)', self.reference)
return True
status = tree.get('status')
tx_id = tree.get('id')
+ tx_secret = tree.get("client_secret")
vals = {
- 'date': fields.datetime.now(),
- 'acquirer_reference': tx_id,
+ "date": fields.datetime.now(),
+ "acquirer_reference": tx_id,
+ "stripe_payment_intent": tx_id,
+ "stripe_payment_intent_secret": tx_secret
}
if status == 'succeeded':
self.write(vals)
@@ -293,3 +321,39 @@ class PaymentTokenStripe(models.Model):
'acquirer_ref': cust_resp['id'],
}
return values
+
+ def _stripe_sca_migrate_customer(self):
+ """Migrate a token from the old implementation of Stripe to the SCA one.
+
+ In the old implementation, it was possible to create a valid charge just by
+ giving the customer ref to ask Stripe to use the default source (= default
+ card). Since we have a one-to-one matching between a saved card, this used to
+ work well - but now we need to specify the payment method for each call and so
+ we have to contact stripe to get the default source for the customer and save it
+ in the payment token.
+ This conversion will happen once per token, the first time it gets used following
+ the installation of the module."""
+ self.ensure_one()
+ url = "customers/%s" % (self.acquirer_ref)
+ data = self.acquirer_id._stripe_request(url, method="GET")
+ sources = data.get('sources', {}).get('data', [])
+ pm_ref = False
+ if sources:
+ if len(sources) > 1:
+ _logger.warning('stripe sca customer conversion: there should be a single saved source per customer!')
+ pm_ref = sources[0].get('id')
+ else:
+ url = 'payment_methods'
+ params = {
+ 'type': 'card',
+ 'customer': self.acquirer_ref,
+ }
+ payment_methods = self.acquirer_id._stripe_request(url, params, method='GET')
+ cards = payment_methods.get('data', [])
+ if len(cards) > 1:
+ _logger.warning('stripe sca customer conversion: there should be a single saved source per customer!')
+ pm_ref = cards and cards[0].get('id')
+ if not pm_ref:
+ raise ValidationError(_('Unable to convert Stripe customer for SCA compatibility. Is there at least one card for this customer in the Stripe backend?'))
+ self.stripe_payment_method = pm_ref
+ _logger.info('converted old customer ref to sca-compatible record for payment token %s', self.id)