예제 #1
0
파일: transfer.py 프로젝트: linura/weboob
    def double_auth(self, recipient):
        try:
            form = self.get_form(id='formCache')
        except FormNotFound:
            raise AddRecipientError('form not found')

        self.browser.context = form['context']
        self.browser.dup = form['dup']
        self.browser.logged = 1

        getsigninfo_data = {}
        getsigninfo_data['b64_jeton_transaction'] = form['context']
        getsigninfo_data['action_level'] = self.get_action_level()
        r = self.browser.open(
            'https://particuliers.secure.societegenerale.fr/sec/getsigninfo.json',
            data=getsigninfo_data)
        assert r.page.doc['commun']['statut'] == 'ok'

        recipient = self.get_recipient_object(recipient, get_info=True)
        self.browser.page = None
        if r.page.doc['donnees']['sign_proc'] == 'csa':
            send_data = {}
            send_data['csa_op'] = 'sign'
            send_data['context'] = form['context']
            r = self.browser.open(
                'https://particuliers.secure.societegenerale.fr/sec/csa/send.json',
                data=send_data)
            assert r.page.doc['commun']['statut'] == 'ok'
            raise AddRecipientStep(
                recipient,
                Value(
                    'code',
                    label=
                    u'Cette opération doit être validée par un Code Sécurité.')
            )
        elif r.page.doc['donnees']['sign_proc'] == 'OOB':
            oob_data = {}
            oob_data['b64_jeton_transaction'] = form['context']
            r = self.browser.open(
                'https://particuliers.secure.societegenerale.fr/sec/oob_sendoob.json',
                data=oob_data)
            assert r.page.doc['commun']['statut'] == 'ok'
            self.browser.id_transaction = r.page.doc['donnees'][
                'id-transaction']
            raise AddRecipientStep(
                recipient,
                ValueBool(
                    'pass',
                    label=
                    u'Valider cette opération sur votre applicaton société générale'
                ))
        else:
            raise AddRecipientError('sign process unknown')
예제 #2
0
파일: browser.py 프로젝트: guix77/weboob
    def new_recipient(self, recipient, **params):
        if 'code' in params:
            # for sms authentication
            return self.send_code(recipient, **params)

        # prepare commun data for all authentication method
        data = {}
        data['adresseBeneficiaire'] = ''
        data['iban'] = recipient.iban
        data['libelleBeneficiaire'] = recipient.label
        data['notification'] = True
        data['typeBeneficiaire'] = ''

        # provisional
        if self.digital_key:
            if 'digital_key' in params:
                return self.new_recipient_digital_key(recipient, data)

        # need to be on recipient page send sms or mobile notification
        # needed to get the phone number, enabling the possibility to send sms.
        # all users with validated phone number can receive sms code
        self.recipients.go(data=JSON({'type': 'TOUS'}))

        # check type of recipient activation
        type_activation = 'sms'

        # provisional
        if self.digital_key:
            if self.page.has_digital_key():
                # force users with digital key activated to use digital key authentication
                type_activation = 'digital_key'

        if type_activation == 'sms':
            # post recipient data sending sms with same request
            data['typeEnvoi'] = 'SMS'
            recipient = self.add_recip.go(data=json.dumps(data),
                                          headers={
                                              'Content-Type':
                                              'application/json'
                                          }).get_recipient(recipient)
            raise AddRecipientStep(
                recipient,
                Value('code', label='Saisissez le code reçu par SMS.'))
        elif type_activation == 'digital_key':
            # recipient validated with digital key are immediatly available
            recipient.enabled_date = datetime.today()
            raise AddRecipientStep(
                recipient,
                ValueBool(
                    'digital_key',
                    label=
                    'Validez pour recevoir une demande sur votre application bancaire. La validation de votre bénéficiaire peut prendre plusieurs minutes.'
                ))
예제 #3
0
파일: browser.py 프로젝트: antibios/weboob
    def new_recipient(self, recipient, **params):
        if 'sms_password' in params:
            return self.end_sms_recipient(recipient, **params)

        if 'otp_sms' in params:
            transactionid = re.search(r'transactionID=(.*)',
                                      self.page.url).group(1)
            self.request_sms.go(param=transactionid)
            validation = {}
            validation['validate'] = {}
            key = self.page.validate_key()
            validation['validate'][key] = []
            inner_param = {}
            inner_param['id'] = self.page.validation_id(key)
            inner_param['type'] = 'SMS'
            inner_param['otp_sms'] = params['otp_sms']
            validation['validate'][key].append(inner_param)
            headers = {
                'Content-Type': 'application/json',
                'Accept': 'application/json, text/plain, */*'
            }
            self.location(self.url + '/step',
                          data=json.dumps(validation),
                          headers=headers)
            saml = self.page.get_saml()
            action = self.page.get_action()
            self.location(action, data={'SAMLResponse': saml})
            if self.authent.is_here():
                self.page.go_on()
                return self.facto_post_recip(recipient)

        self.pre_transfer(
            next(acc for acc in self.get_accounts_list()
                 if acc.type in (Account.TYPE_CHECKING, Account.TYPE_SAVINGS)))
        # This send sms to user.
        self.page.go_add_recipient()
        if self.sms_option.is_here():
            raise AddRecipientStep(
                self.get_recipient_obj(recipient),
                Value(
                    'otp_sms',
                    label=
                    u'Veuillez renseigner le mot de passe unique qui vous a été envoyé par SMS dans le champ réponse.'
                ))

        else:
            self.page.check_canceled_auth()
            self.page.set_browser_form()
            raise AddRecipientStep(
                self.get_recipient_obj(recipient),
                Value('sms_password', label=self.page.get_prompt_text()))
예제 #4
0
    def new_recipient(self, recipient, **params):
        if 'code' in params:
            return self.send_code(recipient, **params)

        if recipient.iban[:2] not in ('FR', 'MC'):
            raise AddRecipientBankError(
                message=u"LCL n'accepte que les iban commençant par MC ou FR.")

        for _ in range(2):
            self.add_recip.go()
            if self.add_recip.is_here():
                break

        if self.no_perm.is_here() and self.page.get_error_msg():
            raise AddRecipientBankError(message=self.page.get_error_msg())

        assert self.add_recip.is_here(), 'Navigation failed: not on add_recip'
        self.page.validate(recipient.iban, recipient.label)

        assert self.recip_confirm.is_here(
        ), 'Navigation failed: not on recip_confirm'
        self.page.check_values(recipient.iban, recipient.label)

        # Send sms to user.
        self.open('/outil/UWBE/Otp/envoiCodeOtp?telChoisi=MOBILE')
        raise AddRecipientStep(
            self.get_recipient_object(recipient.iban, recipient.label),
            Value('code', label='Saisissez le code.'))
예제 #5
0
    def new_recipient(self, recipient, **kwargs):
        if 'code' in kwargs:
            assert self.rcpt_page.is_here()
            assert self.page.is_confirm_sms()

            self.page.confirm_sms(kwargs['code'])
            return self.rcpt_after_sms()

        account = None
        for account in self.get_accounts_list():
            if account.url:
                break

        suffix = 'virements/comptes-externes/nouveau'
        if account.url.endswith('/'):
            target = account.url + suffix
        else:
            target = account.url + '/' + suffix

        self.location(target)
        assert self.page.is_charac()

        self.page.submit_recipient(recipient)

        if self.page.is_send_sms():
            self.page.send_sms()
            assert self.page.is_confirm_sms()
            raise AddRecipientStep(
                self.build_recipient(recipient),
                Value('code', label='Veuillez saisir le code'))
        # if the add recipient is restarted after the sms has been confirmed recently, the sms step is not presented again

        return self.rcpt_after_sms()
예제 #6
0
파일: browser.py 프로젝트: antibios/weboob
 def new_recipient(self, recipient, **params):
     if 'code' in params:
         return self.send_code(recipient, **params)
     try:
         assert recipient.iban[:2] in ['FR', 'MC']
     except AssertionError:
         raise AddRecipientError(
             u"LCL n'accepte que les iban commençant par MC ou FR.")
     for _ in range(2):
         self.add_recip.go()
         if self.add_recip.is_here():
             break
     try:
         assert self.add_recip.is_here()
     except AssertionError:
         raise AddRecipientError('Navigation failed: not on add_recip.')
     self.page.validate(recipient.iban, recipient.label)
     try:
         assert self.recip_confirm.is_here()
     except AssertionError:
         raise AddRecipientError('Navigation failed: not on recip_confirm.')
     self.page.check_values(recipient.iban, recipient.label)
     # Send sms to user.
     self.open('/outil/UWBE/Otp/envoiCodeOtp?telChoisi=MOBILE')
     raise AddRecipientStep(
         self.get_recipient_object(recipient.iban, recipient.label),
         Value('code', label='Saisissez le code.'))
예제 #7
0
파일: browser.py 프로젝트: guix77/weboob
    def new_recipient(self, recipient, **params):
        if 'sms_password' in params:
            return self.end_sms_recipient(recipient, **params)

        if 'otp_sms' in params:
            self.otp_sms_validation(params['otp_sms'])

            if self.authent.is_here():
                self.page.go_on()
                return self.facto_post_recip(recipient)

        if 'pro_password' in params:
            return self.end_pro_recipient(recipient, **params)

        self.pre_transfer(
            next(acc for acc in self.get_accounts_list()
                 if acc.type in (Account.TYPE_CHECKING, Account.TYPE_SAVINGS)))
        # This send sms to user.
        self.page.go_add_recipient()

        if self.sms_option.is_here():
            self.is_send_sms = True
            raise AddRecipientStep(
                self.get_recipient_obj(recipient),
                Value(
                    'otp_sms',
                    label=
                    'Veuillez renseigner le mot de passe unique qui vous a été envoyé par SMS dans le champ réponse.'
                ))

        # pro add recipient.
        elif self.page.need_auth():
            self.page.set_browser_form()
            raise AddRecipientStep(
                self.get_recipient_obj(recipient),
                Value('pro_password', label=self.page.get_prompt_text()))

        else:
            self.page.check_canceled_auth()
            self.page.set_browser_form()
            raise AddRecipientStep(
                self.get_recipient_obj(recipient),
                Value('sms_password', label=self.page.get_prompt_text()))
예제 #8
0
 def send_sms_to_user(self, recipient):
     """Add recipient with OTP SMS authentication"""
     data = {}
     data['csa_op'] = 'sign'
     data['context'] = self.context
     self.open(self.absurl('/sec/csa/send.json'), data=data)
     raise AddRecipientStep(
         recipient,
         Value(
             'code',
             label='Cette opération doit être validée par un Code Sécurité.'
         ))
예제 #9
0
 def send_notif_to_user(self, recipient):
     """Add recipient with 'pass sécurité' authentication"""
     data = {}
     data['b64_jeton_transaction'] = self.context
     r = self.open(self.absurl('/sec/oob_sendoob.json'), data=data)
     self.id_transaction = r.page.get_transaction_id()
     raise AddRecipientStep(
         recipient,
         ValueBool(
             'pass',
             label=
             'Valider cette opération sur votre applicaton société générale'
         ))
예제 #10
0
    def new_recipient(self, recipient, **params):
        if 'sms_password' in params:
            return self.end_sms_recipient(recipient, **params)

        self.pre_transfer(
            next(acc for acc in self.get_accounts_list()
                 if acc.type in (Account.TYPE_CHECKING, Account.TYPE_SAVINGS)))
        # This send sms to user.
        self.page.go_add_recipient()
        self.page.check_canceled_auth()
        self.page.set_browser_form()
        raise AddRecipientStep(
            self.get_recipient_obj(recipient),
            Value('sms_password', label=self.page.get_prompt_text()))
예제 #11
0
 def new_recipient(self, recipient, **params):
     if 'code' in params:
         return self.send_code(recipient, **params)
     # needed to get the phone number, enabling the possibility to send sms.
     self.recipients.go(data=JSON({'type': 'TOUS'}))
     # post recipient data sending sms with same request
     data = {}
     data['adresseBeneficiaire'] = ''
     data['iban'] = recipient.iban
     data['libelleBeneficiaire'] = recipient.label
     data['notification'] = True
     data['typeBeneficiaire'] = ''
     data['typeEnvoi'] = 'SMS'
     recipient = self.add_recip.go(data=json.dumps(data), headers={'Content-Type': 'application/json'}).get_recipient(recipient)
     raise AddRecipientStep(recipient, Value('code', label='Saississez le code.'))
예제 #12
0
    def send_sms_to_user(self, recipient, sms_info):
        """Add recipient with OTP SMS authentication"""
        data = {
            'channelType': sms_info['type'],
            'externalAccountsRequest': self.add_recipient_info,
            'sensitiveOperationAction': 'ADD_TRANSFER_BENEFICIARY',
        }

        phone_id = sms_info['phone']
        data['channelValue'] = phone_id
        self.add_recipient_info['phoneUid'] = phone_id

        self.location(self.absurl('/secure/api-v1/sca/sendOtp', base=True), json=data)
        self.need_reload_state = True
        raise AddRecipientStep(recipient, Value('code', label='Veuillez saisir le code temporaire envoyé par SMS'))
예제 #13
0
    def new_recipient(self, recipient, **params):
        if self.currentSubBank is None:
            self.getCurrentSubBank()

        self.recipients_list.go(subbank=self.currentSubBank)
        if self.page.has_list():
            assert recipient.category in self.page.get_recipients_list(), \
                'Recipient category is not on the website available list.'
            self.page.go_list(recipient.category)

        self.page.go_to_add()
        if self.verify_pass.is_here():
            self.recipient_form = self.page.get_recipient_form()
            raise AddRecipientStep(self.get_recipient_object(recipient), Value('Clé', label=self.page.get_question()))
        else:
            return self.continue_new_recipient(recipient, **params)
예제 #14
0
파일: browser.py 프로젝트: guix77/weboob
    def new_recipient(self, recipient, is_bp_account=False, **kwargs):
        if 'code' in kwargs:
            assert self.rcpt_code.is_here()

            self.post_code(kwargs['code'])
            self.recipient_form = None
            assert self.rcpt_summary.is_here()
            return self.build_recipient(recipient)

        self.create_recipient.go().choose_country(recipient, is_bp_account)
        self.page.populate(recipient)
        if self.page.is_bp_account():
            return self.new_recipient(recipient, is_bp_account=True, **kwargs)

        # send sms
        self.location(self.page.get_confirm_link())
        self.page.set_browser_form()
        raise AddRecipientStep(self.build_recipient(recipient), Value('code', label='Veuillez saisir votre code de validation'))
예제 #15
0
 def new_recipient(self, recipient, **params):
     if 'code' in params:
         # to drop and use self.add_recipient_form instead in send_code()
         recipient_form = json.loads(self.add_recipient_form)
         self.send_code(recipient_form, params['code'])
         if self.page.rcpt_after_sms():
             self.need_reload_state = None
             return self.copy_recipient(recipient)
         elif self.page.is_code_expired():
             self.need_reload_state = True
             raise AddRecipientStep(
                 recipient,
                 Value(
                     'code',
                     label=
                     'Le code sécurité est expiré. Veuillez saisir le nouveau code reçu qui sera valable 5 minutes.'
                 ))
         assert False, self.page.get_error()
     return self.new_recipient_before_otp(recipient, **params)
예제 #16
0
파일: browser.py 프로젝트: linura/weboob
 def new_recipient(self, recipient, **params):
     if self.currentSubBank is None:
         self.getCurrentSubBank()
     if 'Bic' in params:
         return self.post_with_bic(recipient, **params)
     if 'code' in params:
         return self.end_new_recipient(recipient, **params)
     if u'Clé' in params:
         return self.continue_new_recipient(recipient, **params)
     self.recipients_list.go(subbank=self.currentSubBank)
     if self.page.has_list():
         if recipient.category not in self.page.get_recipients_list():
             raise AddRecipientError(
                 'Recipient category is not on the website available list.')
         self.page.go_list(recipient.category)
     self.page.go_to_add()
     if self.verify_pass.is_here():
         raise AddRecipientStep(
             self.get_recipient_object(recipient),
             Value(u'Clé', label=self.page.get_question()))
     else:
         return self.continue_new_recipient(recipient, **params)
예제 #17
0
    def new_recipient(self, recipient, **params):
        if 'code' in params:
            self.page.send_code(params['code'])
            return self.rcpt_after_sms(recipient)

        self.recipients.go()
        self.page.go_add_new_recipient_page()

        if self.recipient_confirmation_page.is_here():
            # Confirm that user want to add recipient
            self.page.continue_new_recipient()

        if self.recipient_confirmation_page.is_here():
            self.page.check_errors()

        assert self.add_recipient.is_here()
        self.page.set_new_recipient_iban(recipient.iban)
        rcpt = self.copy_recipient_obj(recipient)
        # This send the sms to user
        self.page.set_new_recipient_label(recipient.label)

        raise AddRecipientStep(rcpt, Value('code', label='Veuillez entrer le code reçu par SMS.'))
예제 #18
0
    def new_recipient_before_otp(self, recipient, **params):
        self.recipients.go()
        self.page.check_external_iban_form(recipient)
        self.page.check_recipient_iban()

        # fill form
        self.page.fill_recipient_form(recipient)
        rcpt = self.page.get_new_recipient(recipient)

        # get first part of confirm form
        send_code_form = self.page.get_send_code_form()

        data = {
            'appelAjax': 'true',
            'domicileUpdated': 'false',
            'numeroSelectionne.value': '',
            'portableUpdated': 'false',
            'proUpdated': 'false',
            'typeOperationSensible': 'AJOUT_BENEFICIAIRE'
        }
        # this send sms to user
        self.location(self.absurl(
            '/fr/prive/appel-securite-forte-otp-bankone.jsp', base=True),
                      data=data)
        # get second part of confirm form
        send_code_form.update(self.page.get_send_code_form_input())

        # save form value and url for statesmixin
        self.add_recipient_form = dict(send_code_form)
        self.add_recipient_form.update({'url': send_code_form.url})

        # storage can't handle dict with '.' in key
        # to drop when dict with '.' in key is handled
        self.add_recipient_form = json.dumps(self.add_recipient_form)

        self.need_reload_state = True
        raise AddRecipientStep(
            rcpt, Value('code', label='Veuillez saisir le code reçu.'))
예제 #19
0
    def init_new_recipient(self, recipient):
        self.recipient_form = None  # so it is reset when a new recipient is added

        # get url
        account = None
        for account in self.get_accounts_list():
            if account.url:
                break

        suffix = 'virements/comptes-externes/nouveau'
        if account.url.endswith('/'):
            target = account.url + suffix
        else:
            target = account.url + '/' + suffix

        self.location(target)
        assert self.page.is_charac(), 'Not on the page to add recipients.'

        # fill recipient form
        self.page.submit_recipient(recipient)
        recipient.origin_account_id = account.id

        # confirm sending sms
        assert self.page.is_confirm_send_sms(), 'Cannot reach the page asking to send a sms.'
        self.page.confirm_send_sms()

        if self.page.is_send_sms():
            # send sms
            self.page.send_sms()
            assert self.page.is_confirm_sms(), 'The sms was not send.'

            self.recipient_form = self.page.get_confirm_sms_form()
            self.recipient_form['account_url'] = account.url
            raise AddRecipientStep(recipient, Value('code', label='Veuillez saisir le code'))

        # if the add recipient is restarted after the sms has been confirmed recently, the sms step is not presented again
        return self.rcpt_after_sms()
예제 #20
0
파일: browser.py 프로젝트: antibios/weboob
    def new_recipient(self, recipient, **params):
        if not re.match(u"^[-+.,:/?() éèêëïîñàâäãöôòõùûüÿ0-9a-z']+$",
                        recipient.label, re.I):
            raise RecipientInvalidLabel(
                'Recipient label contains invalid characters')

        if 'sms_code' in params and not re.match(r'^[a-z0-9]{6}$',
                                                 params['sms_code'], re.I):
            raise AddRecipientError('SMS verification code is invalid')

        self.transfer_init_page.go(sag=self.sag)
        self.location(self.page.url_list_recipients())
        self.location(self.page.url_add_recipient())

        if not ('sms_code' in params and self.page.can_send_code()):
            self.page.send_sms()
            # go to a GET page, so StatesMixin can reload it
            self.location(self.accounts_url.format(self.sag))
            raise AddRecipientStep(
                self.build_recipient(recipient),
                Value('sms_code', label='Veuillez saisir le code SMS'))
        else:
            self.page.submit_code(params['sms_code'])

            err = hasattr(self.page,
                          'get_sms_error') and self.page.get_sms_error()
            if err:
                raise AddRecipientError(message=err)

            self.page.submit_recipient(recipient.label, recipient.iban)
            self.page.confirm_recipient()
            self.page.check_recipient_error()

            res = self.page.find_recipient(recipient.iban)
            if res is None:
                raise AddRecipientError('Recipient could not be found')
            return res
예제 #21
0
    def new_recipient(self, recipient, **params):
        if 'code' in params:
            self.validate_rcpt_with_sms(params['code'])
            return self.page.rcpt_after_sms(recipient)

        data = {
            'n_nbOccurences': 1000,
            'n_nbOccurences_affichees': 0,
            'n_rang': 0,
        }
        self.recipients.go(data=data)

        step_urls = {
            'first_recipient_check': self.absurl('/ord-web/ord//ord-valider-destinataire-avant-maj.json', base=True),
            'get_bic': self.absurl('/ord-web/ord//ord-tiers-calcul-bic.json', base=True),
            'get_token': self.absurl('/ord-web/ord//ord-preparer-signature-destinataire.json', base=True),
            'get_sign_info': self.absurl('/sec/getsigninfo.json', base=True),
            'send_otp_to_user': self.absurl('/sec/csa/send.json', base=True),
        }

        self.add_recipient.go(method='POST', headers={'Content-Type': 'application/json;charset=UTF-8'})
        countries = self.page.get_countries()

        # first recipient check
        data = {
            'an_codeAction': 'ajout_tiers',
            'an_refSICoordonnee': '',
            'an_refSITiers': '',
            'cl_iban': recipient.iban,
            'cl_raisonSociale': recipient.label,
        }
        self.location(step_urls['first_recipient_check'], data=data)

        # get bic
        data = {
            'an_activateCMU': 'true',
            'an_codePaysBanque': '',
            'an_nature': 'C',
            'an_numeroCompte': recipient.iban,
            'an_topIBAN': 'true',
            'cl_adresse': '',
            'cl_adresseBanque': '',
            'cl_codePays': recipient.iban[:2],
            'cl_libellePaysBanque': '',
            'cl_libellePaysDestinataire': countries[recipient.iban[:2]],
            'cl_nomBanque': '',
            'cl_nomRaisonSociale': recipient.label,
            'cl_ville': '',
            'cl_villeBanque': '',
        }
        self.location(step_urls['get_bic'], data=data)
        bic = self.page.get_response_data()

        # get token
        data = {
            'an_coordonnee_codePaysBanque': '',
            'an_coordonnee_nature': 'C',
            'an_coordonnee_numeroCompte': recipient.iban,
            'an_coordonnee_topConfidentiel': 'false',
            'an_coordonnee_topIBAN': 'true',
            'an_refSICoordonnee': '',
            'an_refSIDestinataire': '',
            'cl_adresse': '',
            'cl_codePays': recipient.iban[:2],
            'cl_coordonnee_adresseBanque': '',
            'cl_coordonnee_bic': bic,
            'cl_coordonnee_categories_libelle': '',
            'cl_coordonnee_categories_refSi': '',
            'cl_coordonnee_libellePaysBanque': '',
            'cl_coordonnee_nomBanque': '',
            'cl_coordonnee_villeBanque': '',
            'cl_libellePaysDestinataire': countries[recipient.iban[:2]],
            'cl_nomRaisonSociale': recipient.label,
            'cl_ville': '',
        }
        self.location(step_urls['get_token'], data=data)
        self.new_rcpt_validate_form = data
        payload = self.page.get_response_data()

        # get sign info
        data = {
            'b64_jeton_transaction': payload['jeton'],
            'action_level': payload['sensibilite'],
        }
        self.location(step_urls['get_sign_info'], data=data)

        # send otp to user
        data = {
            'context': payload['jeton'],
            'csa_op': 'sign'
        }
        self.location(step_urls['send_otp_to_user'], data=data)
        self.new_rcpt_validate_form.update(data)

        rcpt = self.copy_recipient_obj(recipient)
        raise AddRecipientStep(rcpt, Value('code', label='Veuillez entrer le code reçu par SMS.'))
예제 #22
0
    def new_recipient(self, recipient, **params):
        if not re.match(u"^[-+.,:/?() éèêëïîñàâäãöôòõùûüÿ0-9a-z']+$",
                        recipient.label, re.I):
            raise RecipientInvalidLabel(
                'Recipient label contains invalid characters')

        if 'sms_code' in params and not re.match(r'^[a-z0-9]{6}$',
                                                 params['sms_code'], re.I):
            # check before send sms code because it can crash website if code is invalid
            raise AddRecipientBankError("SMS code %s is invalid" %
                                        params['sms_code'])

        # avoid `iter_accounts` if there is only one perimeter
        if len(self.perimeters) > 1:
            accounts = list(self.iter_accounts())
            assert recipient.origin_account_id, 'Origin account id is mandatory for multispace'
            account = find_object(accounts,
                                  id=recipient.origin_account_id,
                                  error=AccountNotFound)
            self.go_to_perimeter(account._perimeter)

        self.transfer_init_page.go()
        assert self.transfer_init_page.is_here()

        if not self.page.add_recipient_is_allowed():
            if not [
                    rec for rec in self.page.iter_recipients()
                    if rec.category == 'Externe'
            ]:
                raise AddRecipientBankError(
                    'Vous ne pouvez pas ajouter de bénéficiaires, veuillez contacter votre banque.'
                )
            assert False, 'Xpath for a recipient add is not catched'

        self.location(self.page.url_list_recipients())
        # there are 2 pages from where we can add a new recipient:
        # - RecipientListPage, but the link is sometimes missing
        # - TransferPage, start making a transfer with a new recipient but don't complete the transfer
        #   but it seems dangerous since we have to set an amount, etc.
        # so we implement it in 2 ways with a preference for RecipientListPage
        if self.page.url_add_recipient():
            self.logger.debug(
                'good, we can add a recipient from the recipient list')
        else:
            # in this case, the link was missing
            self.logger.warning(
                'cannot add a recipient from the recipient list page, pretending to make a transfer in order to add it'
            )
            self.transfer_init_page.go()
            assert self.transfer_init_page.is_here()

        self.location(self.page.url_add_recipient())

        if not ('sms_code' in params and self.page.can_send_code()):
            self.page.send_sms()
            # go to a GET page, so StatesMixin can reload it
            self.accounts.go()
            raise AddRecipientStep(
                self.build_recipient(recipient),
                Value('sms_code', label='Veuillez saisir le code SMS'))
        else:
            self.page.submit_code(params['sms_code'])

            err = hasattr(self.page,
                          'get_sms_error') and self.page.get_sms_error()
            if err:
                raise AddRecipientBankError(message=err)

            self.page.submit_recipient(recipient.label, recipient.iban)
            self.page.confirm_recipient()
            self.page.check_recipient_error()
            if self.transfer_page.is_here():
                # in this case, we were pretending to make a transfer, just to add the recipient
                # go back to transfer page to abort the transfer and see the new recipient
                self.transfer_init_page.go()
                assert self.transfer_init_page.is_here()

            res = self.page.find_recipient(recipient.iban)
            assert res, 'Recipient with iban %s could not be found' % recipient.iban
            return res