Repository: mozmeao/basket

Issue:
Put FxA user data into SFDC in addition to SFMC
FxA users will soon need to go into SFDC instead of the data extension in SFMC into which they're going now. The timing for this will need to be coordinated with LCM and the IT Salesforce folks.


Discussion:
There are some questions I have for this move and I just wanted to record them here. The current code is here for the record:

https://github.com/mozmeao/basket/blob/1a4dc149b25c789ca7539a845f80aea08947038f/basket/news/tasks.py#L339-L351

1. We are currently receiving and storing a `service` value from FxA. Do we have a field for that in SFDC? Should we?
2. We are currently storing last-login information in SFMC. Will that move too?
3. We are currently storing information about devices in SFMC. Will that move too?
4. We currently store a language that comes from FxA. Do we need to store that separately from the current language field on the Contact, or should this update that field?
5. What should we do with the email change data we get from FxA?
6. What should we do with the account deletion data we get from FxA?

I'll add to this list if more come up. Let's discuss these on this issue.
> 
> 
> There are some questions I have for this move and I just wanted to record them here. The current code is here for the record:
> 
> https://github.com/mozmeao/basket/blob/1a4dc149b25c789ca7539a845f80aea08947038f/basket/news/tasks.py#L339-L351
> 
>     1. We are currently receiving and storing a `service` value from FxA. Do we have a field for that in SFDC? Should we?

We should. Generally product information will be stored and queried in the CDP, but service has become a vital stat about a new user, as vital as language or email addy. 
> 
>     2. We are currently storing last-login information in SFMC. Will that move too?

No. My understanding, and let's please confirm with Andrew, but data like last login will continue to feed to marketing cloud where it will be fed to the CDP. Same thing with the "Sync" logins information.

SFDC will be where we store the basic information only about each account, fxa-id, email, language, service, created date. The rest of the data will be compiled and queried in the CDP.
 
> 
>     3. We are currently storing information about devices in SFMC. Will that move too?

See above.
> 
>     4. We currently store a language that comes from FxA. Do we need to store that separately from the current language field on the Contact, or should this update that field?

I think we should store both a newsletter language and an FxA language. This is my opinion but I think it's right. 
> 
>     5. What should we do with the email change data we get from FxA?

Lisa, Andrew and I have discussed storing additional email addresses on a SFDC record. We will need to create a SFDC field and send email change data to the CRM. We want to keep all email records, but know which is the appropriate one to use for contacting a user. 
> 
>     6. What should we do with the account deletion data we get from FxA?
> 
Ideally we should delete the account. But for now, let's store a boolean flag on the CRM that defaults to false for everyone. If an account is deleted we can flip to TRUE and use that to come up with a process for purging the record from the system.

> 
> I'll add to this list if more come up. Let's discuss these on this issue.

Cool. Happy to help in any way I can. 
And a question for you @pmac : every account going into SFDC will need a token or key that we can use to power their preference center page. (This eliminates using email and therefor the login for each user). Would basket generate that or CRM?
Currently when basket adds a user to SFDC it will generate a token for them, but I believe there's also some magic on the SFDC side that will generate one if a contact is added without one. So either way every contact in SFDC should have one and so the pref center page should work normally for everyone once we get them all in there.
> > There are some questions I have for this move and I just wanted to record them here. The current code is here for the record:
> > https://github.com/mozmeao/basket/blob/1a4dc149b25c789ca7539a845f80aea08947038f/basket/news/tasks.py#L339-L351
> > 
> > ```
> > 1. We are currently receiving and storing a `service` value from FxA. Do we have a field for that in SFDC? Should we?
> > ```
> 
> We should. Generally product information will be stored and queried in the CDP, but service has become a vital stat about a new user, as vital as language or email addy.
> 
@mozbrandon Will need to create a text field called 'FirstService' for this in Sandbox. This is a one time value indicating what service they (first) came in with
> > ```
> > 2. We are currently storing last-login information in SFMC. Will that move too?
> > ```
> 
> No. My understanding, and let's please confirm with Andrew, but data like last login will continue to feed to marketing cloud where it will be fed to the CDP. Same thing with the "Sync" logins information.
> 
Correct. For now, we want to keep parallel feeds in both Marketing Cloud and now this new feed in SalesCloud. Once we have tested Lauren and teams workflows, and we have tested the BigQuery feed, we can turn off the Basket FxA feed to SFMC. @mozbrandon - No action on your part
> SFDC will be where we store the basic information only about each account, fxa-id, email, language, service, created date. The rest of the data will be compiled and queried in the CDP.
> 
> > ```
> > 3. We are currently storing information about devices in SFMC. Will that move too?
> > ```
> 
> See above.
> 
> > ```
No device data will come in. @mozbrandon - No action

> > 4. We currently store a language that comes from FxA. Do we need to store that separately from the current language field on the Contact, or should this update that field?
> > ```
> 
> I think we should store both a newsletter language and an FxA language. This is my opinion but I think it's right.
> 
@mozbrandon Will need to create a field for this in Sandbox called FxA language.
> > ```
> > 5. What should we do with the email change data we get from FxA?
> > ```
> 
> Lisa, Andrew and I have discussed storing additional email addresses on a SFDC record. We will need to create a SFDC field and send email change data to the CRM. We want to keep all email records, but know which is the appropriate one to use for contacting a user.
> 
> > ```
I talked with Lauren and we do not need the multiple email addresses right now. We will handle that setup at a later time. @mozbrandon - No action for you right now. 

> > 6. What should we do with the account deletion data we get from FxA?
> > ```
> 
> Ideally we should delete the account. But for now, let's store a boolean flag on the CRM that defaults to false for everyone. If an account is deleted we can flip to TRUE and use that to come up with a process for purging the record from the system.
> 
I agree with this approach. @mozbrandon - Will need to create the boolean 'FxA Account Deleted?' 

> > I'll add to this list if more come up. Let's discuss these on this issue.
> 
> Cool. Happy to help in any way I can.


All that sounds great to me. @mozbrandon just get me the names of the new fields and I should be able to get a PR in fairly quickly.
@pmac the following fields were created in advo:

`FirstService__c` text 
`FxA_Language__c` text 
`FxA_Account_Deleted__c` boolean

I also just realized that we currently optionally store a "Created Date" for the FxA user if it's send in the data from FxA. Do we need a separate FxA_Created_Date field for this, or are we happy with just the Contact record dates we already have?
Brandon - Please create a date field. Lets grab it now while we can. Can
always scrap it later.

On Mon, Aug 26, 2019 at 1:17 PM Paul McLanahan <notifications@github.com>
wrote:

> I also just realized that we currently optionally store a "Created Date"
> for the FxA user if it's send in the data from FxA. Do we need a separate
> FxA_Created_Date field for this, or are we happy with just the Contact
> record dates we already have?
>
> —
> You are receiving this because you commented.
> Reply to this email directly, view it on GitHub
> <https://github.com/mozmeao/basket/issues/149?email_source=notifications&email_token=ANAFD3P57XVG4AA7E24ASM3QGQ24FA5CNFSM4HDWT3EKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD5FRNPQ#issuecomment-525014718>,
> or mute the thread
> <https://github.com/notifications/unsubscribe-auth/ANAFD3KHH3EJ4NYR2WCYQPDQGQ24FANCNFSM4HDWT3EA>
> .
>


-- 
Andrew Morales
Mozilla - CRM Manager

@pmac 
`FxA_Created_Date__c` Date (not Date/Time)
the PR is in #318 . Should be reviewed and merged shortly, and then we can test in basket-dev.
Do we want to change the name of the issue to reflect that we are (not)
going to stop the feed to SFMC in the near future and that we are instead
going to start the net-new FxA feed in SF?

On Wed, Aug 28, 2019 at 1:08 PM Paul McLanahan <notifications@github.com>
wrote:

> the PR is in #318 <https://github.com/mozmeao/basket/pull/318> . Should
> be reviewed and merged shortly, and then we can test in basket-dev.
>
> —
> You are receiving this because you commented.
> Reply to this email directly, view it on GitHub
> <https://github.com/mozmeao/basket/issues/149?email_source=notifications&email_token=ANAFD3P6X654QS4AMCYJCWLQG3LK3A5CNFSM4HDWT3EKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD5MKDRI#issuecomment-525902277>,
> or mute the thread
> <https://github.com/notifications/unsubscribe-auth/ANAFD3K3YYPCUKNE23B4AP3QG3LK3ANCNFSM4HDWT3EA>
> .
>


-- 
Andrew Morales
Mozilla - CRM Manager


Resolving patch:
diff --git a/basket/news/backends/sfdc.py b/basket/news/backends/sfdc.py
--- a/basket/news/backends/sfdc.py
+++ b/basket/news/backends/sfdc.py
@@ -78,6 +78,9 @@ def cast_lower(value):
     'payee_id': 'PMT_Cust_Id__c',
     'fxa_id': 'FxA_Id__c',
     'fxa_deleted': 'FxA_Account_Deleted__c',
+    'fxa_lang': 'FxA_Language__c',
+    'fxa_service': 'FirstService__c',
+    'fxa_create_date': 'FxA_Created_Date__c',
     'fpn_country': 'FPN_Waitlist_Geo__c',
     'fpn_platform': 'FPN_Waitlist_Platform__c',
 }
diff --git a/basket/news/tasks.py b/basket/news/tasks.py
--- a/basket/news/tasks.py
+++ b/basket/news/tasks.py
@@ -27,7 +27,8 @@
 from basket.news.celery import app as celery_app
 from basket.news.models import (FailedTask, Newsletter, Interest,
                                 QueuedTask, TransactionalEmailMessage)
-from basket.news.newsletters import get_sms_vendor_id, get_transactional_message_ids, newsletter_map
+from basket.news.newsletters import (get_sms_vendor_id, get_transactional_message_ids, newsletter_map,
+                                     newsletter_languages)
 from basket.news.utils import (cents_to_dollars, generate_token, get_accept_languages, get_best_language,
                                get_user_data, iso_format_unix_timestamp, parse_newsletters, parse_newsletters_csv,
                                SUBSCRIBE, UNSUBSCRIBE, get_best_supported_lang, split_name)
@@ -244,34 +245,48 @@ def fxa_delete(data):
 
 @et_task
 def fxa_verified(data):
-    """Add new FxA users to an SFMC data extension"""
-    # used to be handled by the fxa_register view
+    """Add new FxA users to an SFMC data extension and SFDC"""
+    # if we're not using the sandbox ignore testing domains
+    if email_is_testing(data['email']):
+        return
+
+    lang = get_best_language(get_accept_languages(data.get('locale')))
+    if not lang or lang not in newsletter_languages():
+        lang = 'other'
+
+    # avoid modifying the passed in dict
+    new_data = deepcopy(data)
+    new_data['lang'] = lang
+
+    # add the data to SFMC
+    # do this part async so that it can retry independent of the SFDC parts
+    fxa_verified_sfmc.delay(new_data)
+
+    # add the data to SFDC
+    # do this part async so that it can retry independent of the SFMC parts
+    if settings.FXA_EVENTS_VERIFIED_SFDC_ENABLE:
+        fxa_verified_sfdc.delay(new_data)
+
+
+@et_task
+def fxa_verified_sfdc(data):
     email = data['email']
     fxa_id = data['uid']
+    lang = data['lang']
     create_date = data.get('createDate')
-    if create_date:
-        create_date = datetime.fromtimestamp(create_date)
-
-    locale = data.get('locale')
     subscribe = data.get('marketingOptIn')
     newsletters = data.get('newsletters')
     metrics = data.get('metricsContext', {})
-    service = data.get('service', '')
-    country = data.get('countryCode', '')
-
-    if not locale:
-        statsd.incr('fxa_verified.ignored.no_locale')
-        return
-
-    # if we're not using the sandbox ignore testing domains
-    if email_is_testing(email):
-        return
-
-    lang = get_best_language(get_accept_languages(locale))
-    if not lang:
-        return
-
-    _update_fxa_info(email, lang, fxa_id, service, create_date)
+    new_data = {
+        'email': email,
+        'source_url': fxa_source_url(metrics),
+        'country': data.get('countryCode', ''),
+        'fxa_lang': data.get('locale'),
+        'fxa_service': data.get('service', ''),
+        'fxa_id': fxa_id,
+    }
+    if create_date:
+        new_data['fxa_create_date'] = iso_format_unix_timestamp(create_date)
 
     add_news = None
     if newsletters:
@@ -283,15 +298,32 @@ def fxa_verified(data):
         add_news = settings.FXA_REGISTER_NEWSLETTER
 
     if add_news:
-        upsert_user.delay(SUBSCRIBE, {
-            'email': email,
-            'lang': lang,
-            'newsletters': add_news,
-            'source_url': fxa_source_url(metrics),
-            'country': country,
+        new_data['newsletters'] = add_news
+
+    user_data = get_fxa_user_data(fxa_id, email)
+    # don't overwrite the user's language if already set
+    if not (user_data and user_data.get('lang')):
+        new_data['lang'] = lang
+
+    upsert_contact(SUBSCRIBE, new_data, user_data)
+
+
+@et_task
+def fxa_verified_sfmc(data):
+    create_date = data.get('createDate')
+    if create_date:
+        create_date = datetime.fromtimestamp(create_date)
+    try:
+        apply_updates('Firefox_Account_ID', {
+            'EMAIL_ADDRESS_': data['email'],
+            'CREATED_DATE_': gmttime(create_date),
+            'FXA_ID': data['uid'],
+            'FXA_LANGUAGE_ISO2': data['lang'],
+            'SERVICE': data.get('service', ''),
         })
-    else:
-        record_source_url(email, fxa_source_url(metrics), 'fxa-no-optin')
+    except NewsletterException as e:
+        # don't report these errors to sentry until retries exhausted
+        raise RetryTask(str(e))
 
 
 @et_task
@@ -342,21 +374,6 @@ def _add_fxa_activity(data):
     })
 
 
-def _update_fxa_info(email, lang, fxa_id, service, create_date=None):
-    # leaving here because easier to test
-    try:
-        apply_updates('Firefox_Account_ID', {
-            'EMAIL_ADDRESS_': email,
-            'CREATED_DATE_': gmttime(create_date),
-            'FXA_ID': fxa_id,
-            'FXA_LANGUAGE_ISO2': lang,
-            'SERVICE': service,
-        })
-    except NewsletterException as e:
-        # don't report these errors to sentry until retries exhausted
-        raise RetryTask(str(e))
-
-
 @et_task
 def update_get_involved(interest_id, lang, name, email, country, email_format,
                         subscribe, message, source_url):
@@ -741,28 +758,7 @@ def process_subhub_event_customer_created(data):
         'fxa_id': data['user_id'],
         'payee_id': data['customer_id']
     }
-
-    user_data = None
-    # try getting user data with the fxa_id first
-    user_data_fxa = get_user_data(fxa_id=contact_data['fxa_id'],
-                                  extra_fields=['id'])
-    if user_data_fxa:
-        # if the email matches what we got from subhub, which got it from fxa, we're good
-        if user_data_fxa['email'] == data['email']:
-            user_data = user_data_fxa
-        # otherwise we've gotta make sure this one doesn't interfere with us updating or creating
-        # the one with the right email address below
-        else:
-            statsd.incr('news.tasks.process_subhub_event.customer_created.fxa_id_dupe')
-            sfdc.update(user_data_fxa, {
-                'fxa_id': f"DUPE:{contact_data['fxa_id']}",
-                'fxa_deleted': True,
-            })
-
-    # if we still don't have user data try again with email this time
-    if not user_data:
-        user_data = get_user_data(email=data['email'], extra_fields=['id'])
-
+    user_data = get_fxa_user_data(data['user_id'], data['email'])
     if user_data:
         # if user was found in sfdc, see if we should update their name(s)
         # if current last name is '_', update it
@@ -1350,3 +1346,34 @@ def snitch(start_time=None):
     requests.post('https://nosnch.in/{}'.format(snitch_id), data={
         'm': totalms,
     })
+
+
+def get_fxa_user_data(fxa_id, email):
+    """Return a user data dict, just like `get_user_data` below, but ensure we have a good FxA contact
+
+    First look for a user by FxA ID. If we get a user, and the email matches what was passed in, return it.
+    If the email doesn't match, set the first user's FxA_ID to "DUPE:<fxa_id>" so that we don't run into dupe
+    issues, and set "fxa_deleted" to True. Then look up a user with the email address and return that or None.
+    """
+    user_data = None
+    # try getting user data with the fxa_id first
+    user_data_fxa = get_user_data(fxa_id=fxa_id,
+                                  extra_fields=['id'])
+    if user_data_fxa:
+        # if the email matches what we got from subhub, which got it from fxa, we're good
+        if user_data_fxa['email'] == email:
+            user_data = user_data_fxa
+        # otherwise we've gotta make sure this one doesn't interfere with us updating or creating
+        # the one with the right email address below
+        else:
+            statsd.incr('news.utils.get_fxa_user_data.fxa_id_dupe')
+            sfdc.update(user_data_fxa, {
+                'fxa_id': f"DUPE:{fxa_id}",
+                'fxa_deleted': True,
+            })
+
+    # if we still don't have user data try again with email this time
+    if not user_data:
+        user_data = get_user_data(email=email, extra_fields=['id'])
+
+    return user_data
diff --git a/basket/settings.py b/basket/settings.py
--- a/basket/settings.py
+++ b/basket/settings.py
@@ -376,6 +376,7 @@ def get_default_gateway_linux():
 FXA_EVENTS_QUEUE_URL = config('FXA_EVENTS_QUEUE_URL', default='')
 FXA_EVENTS_QUEUE_WAIT_TIME = config('FXA_EVENTS_QUEUE_WAIT_TIME', cast=int, default=10)
 FXA_EVENTS_SNITCH_ID = config('FXA_EVENTS_SNITCH_ID', default='')
+FXA_EVENTS_VERIFIED_SFDC_ENABLE = config('FXA_EVENTS_VERIFIED_SFDC_ENABLE', cast=bool, default=False)
 
 FXA_ACCESS_KEY_ID = config('FXA_ACCESS_KEY_ID', default='')
 FXA_SECRET_ACCESS_KEY = config('FXA_SECRET_ACCESS_KEY', default='')


Test patch:
diff --git a/basket/news/tests/test_tasks.py b/basket/news/tests/test_tasks.py
--- a/basket/news/tests/test_tasks.py
+++ b/basket/news/tests/test_tasks.py
@@ -45,6 +45,7 @@
     get_lock,
     RetryTask,
 )
+from basket.news.utils import iso_format_unix_timestamp
 
 
 @override_settings(TASK_LOCKING_ENABLE=False)
@@ -1189,11 +1190,15 @@ def test_firefox_ios_tablet(self):
         self.assertEqual(record['DEVICE_TYPE'], 'T')
 
 
-@patch('basket.news.tasks._update_fxa_info')
-@patch('basket.news.tasks.upsert_user')
+@override_settings(FXA_EVENTS_VERIFIED_SFDC_ENABLE=True)
+@patch('basket.news.tasks.get_best_language', Mock(return_value='en-US'))
+@patch('basket.news.tasks.newsletter_languages', Mock(return_value=['en-US']))
+@patch('basket.news.tasks.apply_updates')
+@patch('basket.news.tasks.upsert_contact')
+@patch('basket.news.tasks.get_fxa_user_data')
 class FxAVerifiedTests(TestCase):
-    @patch('basket.news.tasks.sfmc')
-    def test_no_subscribe(self, sfmc_mock, upsert_mock, fxa_info_mock):
+    def test_no_subscribe(self, fxa_data_mock, upsert_mock, apply_mock):
+        fxa_data_mock.return_value = None
         data = {
             'email': 'thedude@example.com',
             'uid': 'the-fxa-id',
@@ -1201,10 +1206,25 @@ def test_no_subscribe(self, sfmc_mock, upsert_mock, fxa_info_mock):
             'marketingOptIn': False,
         }
         fxa_verified(data)
-        upsert_mock.delay.assert_not_called()
-        fxa_info_mock.assert_called_with(data['email'], 'en-US', data['uid'], '', None)
+        upsert_mock.assert_called_with(SUBSCRIBE, {
+            'email': data['email'],
+            'source_url': settings.FXA_REGISTER_SOURCE_URL,
+            'country': '',
+            'lang': 'en-US',
+            'fxa_lang': data['locale'],
+            'fxa_service': '',
+            'fxa_id': 'the-fxa-id',
+        }, None)
+        apply_mock.assert_called_with('Firefox_Account_ID', {
+            'EMAIL_ADDRESS_': data['email'],
+            'CREATED_DATE_': gmttime(None),
+            'FXA_ID': 'the-fxa-id',
+            'FXA_LANGUAGE_ISO2': 'en-US',
+            'SERVICE': '',
+        })
 
-    def test_with_subscribe(self, upsert_mock, fxa_info_mock):
+    def test_with_subscribe(self, fxa_data_mock, upsert_mock, apply_mock):
+        fxa_data_mock.return_value = {'lang': 'en-US'}
         data = {
             'email': 'thedude@example.com',
             'uid': 'the-fxa-id',
@@ -1213,16 +1233,25 @@ def test_with_subscribe(self, upsert_mock, fxa_info_mock):
             'service': 'sync',
         }
         fxa_verified(data)
-        upsert_mock.delay.assert_called_once_with(SUBSCRIBE, {
+        upsert_mock.assert_called_with(SUBSCRIBE, {
             'email': data['email'],
-            'lang': 'en-US',
             'newsletters': settings.FXA_REGISTER_NEWSLETTER,
             'source_url': settings.FXA_REGISTER_SOURCE_URL,
             'country': '',
+            'fxa_lang': data['locale'],
+            'fxa_service': 'sync',
+            'fxa_id': 'the-fxa-id',
+        }, fxa_data_mock())
+        apply_mock.assert_called_with('Firefox_Account_ID', {
+            'EMAIL_ADDRESS_': data['email'],
+            'CREATED_DATE_': gmttime(None),
+            'FXA_ID': 'the-fxa-id',
+            'FXA_LANGUAGE_ISO2': 'en-US',
+            'SERVICE': 'sync',
         })
-        fxa_info_mock.assert_called_with(data['email'], 'en-US', data['uid'], 'sync', None)
 
-    def test_with_newsletters(self, upsert_mock, fxa_info_mock):
+    def test_with_newsletters(self, fxa_data_mock, upsert_mock, apply_mock):
+        fxa_data_mock.return_value = None
         data = {
             'email': 'thedude@example.com',
             'uid': 'the-fxa-id',
@@ -1232,16 +1261,26 @@ def test_with_newsletters(self, upsert_mock, fxa_info_mock):
             'service': 'sync',
         }
         fxa_verified(data)
-        upsert_mock.delay.assert_called_once_with(SUBSCRIBE, {
+        upsert_mock.assert_called_with(SUBSCRIBE, {
             'email': data['email'],
-            'lang': 'en-US',
             'newsletters': 'test-pilot,take-action-for-the-internet,' + settings.FXA_REGISTER_NEWSLETTER,
             'source_url': settings.FXA_REGISTER_SOURCE_URL,
             'country': '',
+            'lang': 'en-US',
+            'fxa_lang': data['locale'],
+            'fxa_service': 'sync',
+            'fxa_id': 'the-fxa-id',
+        }, None)
+        apply_mock.assert_called_with('Firefox_Account_ID', {
+            'EMAIL_ADDRESS_': data['email'],
+            'CREATED_DATE_': gmttime(None),
+            'FXA_ID': 'the-fxa-id',
+            'FXA_LANGUAGE_ISO2': 'en-US',
+            'SERVICE': 'sync',
         })
-        fxa_info_mock.assert_called_with(data['email'], 'en-US', data['uid'], 'sync', None)
 
-    def test_with_subscribe_and_metrics(self, upsert_mock, fxa_info_mock):
+    def test_with_subscribe_and_metrics(self, fxa_data_mock, upsert_mock, apply_mock):
+        fxa_data_mock.return_value = None
         data = {
             'email': 'thedude@example.com',
             'uid': 'the-fxa-id',
@@ -1255,27 +1294,51 @@ def test_with_subscribe_and_metrics(self, upsert_mock, fxa_info_mock):
             'countryCode': 'DE',
         }
         fxa_verified(data)
-        upsert_mock.delay.assert_called_with(SUBSCRIBE, {
+        upsert_mock.assert_called_with(SUBSCRIBE, {
             'email': data['email'],
-            'lang': 'en-US',
             'newsletters': settings.FXA_REGISTER_NEWSLETTER,
             'source_url': settings.FXA_REGISTER_SOURCE_URL + '?utm_campaign=bowling',
             'country': 'DE',
+            'lang': 'en-US',
+            'fxa_lang': data['locale'],
+            'fxa_service': 'monitor',
+            'fxa_id': 'the-fxa-id',
+        }, None)
+        apply_mock.assert_called_with('Firefox_Account_ID', {
+            'EMAIL_ADDRESS_': data['email'],
+            'CREATED_DATE_': gmttime(None),
+            'FXA_ID': 'the-fxa-id',
+            'FXA_LANGUAGE_ISO2': 'en-US',
+            'SERVICE': 'monitor',
         })
-        fxa_info_mock.assert_called_with(data['email'], 'en-US', data['uid'], 'monitor', None)
 
-    @patch('basket.news.tasks.sfmc')
-    def test_with_createDate(self, sfmc_mock, upsert_mock, fxa_info_mock):
-        create_date_float = 1526996035.498
-        create_date = datetime.fromtimestamp(create_date_float)
+    def test_with_createDate(self, fxa_data_mock, upsert_mock, apply_mock):
+        fxa_data_mock.return_value = None
+        create_date = 1526996035.498
         data = {
-            'createDate': create_date_float,
+            'createDate': create_date,
             'email': 'thedude@example.com',
             'uid': 'the-fxa-id',
             'locale': 'en-US,en'
         }
         fxa_verified(data)
-        fxa_info_mock.assert_called_with(data['email'], 'en-US', data['uid'], '', create_date)
+        upsert_mock.assert_called_with(SUBSCRIBE, {
+            'email': data['email'],
+            'source_url': settings.FXA_REGISTER_SOURCE_URL,
+            'country': '',
+            'lang': 'en-US',
+            'fxa_lang': data['locale'],
+            'fxa_service': '',
+            'fxa_id': 'the-fxa-id',
+            'fxa_create_date': iso_format_unix_timestamp(create_date)
+        }, None)
+        apply_mock.assert_called_with('Firefox_Account_ID', {
+            'EMAIL_ADDRESS_': data['email'],
+            'CREATED_DATE_': gmttime(datetime.fromtimestamp(create_date)),
+            'FXA_ID': 'the-fxa-id',
+            'FXA_LANGUAGE_ISO2': 'en-US',
+            'SERVICE': '',
+        })
 
 
 @patch('basket.news.tasks.upsert_user')