Вознаграждайте рефералов

Один из самых эффективных способов получить новых пользователей — это реферальные ссылки. Вы можете использовать динамические ссылки вместе с базой данных в реальном времени и облачными функциями для Firebase , чтобы побудить ваших пользователей приглашать своих друзей, предлагая вознаграждения в приложении за успешные рефералы как отправителю, так и получателю.

Ключевые преимущества

  • Ускорьте рост, предоставив вашим пользователям стимул приглашать своих друзей.
  • Ссылки-приглашения работают на разных платформах.
  • Новые пользователи, открывающие ваше приложение в первый раз, получают первый опыт работы, который вы настраиваете для них. Например, вы можете автоматически связать их с другом, который их пригласил.
  • При желании можно отложить предоставление вознаграждений до тех пор, пока новые пользователи не выполнят какое-либо вводное задание, например прохождение обучения.

Вот как начать!

Настройте Firebase и Dynamic Links SDK

Настройте новый проект Firebase и установите Dynamic Links SDK в свое приложение. ( iOS , Android , C++ , Unity ). Установка Dynamic Links SDK позволяет Firebase передавать данные о Dynamic Link приложению, в том числе после того, как пользователь установит приложение. Без SDK невозможно подключить пользователя после установки одним кликом перед установкой.

Чтобы создать приглашение, сначала создайте ссылку, которую получатель открывает, чтобы принять приглашение. Позже вы включите эту ссылку в текст приглашения. Когда получатель приглашения устанавливает ваше приложение, открывая ссылку, он может получить индивидуальный опыт первого запуска, в том числе получить вознаграждение в приложении.

Эта ссылка-приглашение является динамической ссылкой со значением параметра link , указывающим, что она исходит от вашего существующего пользователя.

Существует множество способов отформатировать эти полезные данные параметров link и связать их с вашим приложением. Один из простых способов — указать идентификатор учетной записи отправителя в параметре запроса, как в следующем примере:

https://mygame.example.com/?invitedby=SENDER_UID
.

Затем, чтобы создать динамические ссылки , подходящие для включения в приглашение, вы можете использовать API Dynamic Link Builder:

Быстрый

Примечание. Этот продукт Firebase недоступен для целей macOS, Mac Catalyst, tvOS или watchOS.
guard let uid = Auth.auth().currentUser?.uid else { return }
let link = URL(string: "https://mygame.example.com/?invitedby=\(uid)")
let referralLink = DynamicLinkComponents(link: link!, domain: "example.page.link")

referralLink.iOSParameters = DynamicLinkIOSParameters(bundleID: "com.example.ios")
referralLink.iOSParameters?.minimumAppVersion = "1.0.1"
referralLink.iOSParameters?.appStoreID = "123456789"

referralLink.androidParameters = DynamicLinkAndroidParameters(packageName: "com.example.android")
referralLink.androidParameters?.minimumVersion = 125

referralLink.shorten { (shortURL, warnings, error) in
  if let error = error {
    print(error.localizedDescription)
    return
  }
  self.invitationUrl = shortURL
}

Java

FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
String uid = user.getUid();
String link = "https://mygame.example.com/?invitedby=" + uid;
FirebaseDynamicLinks.getInstance().createDynamicLink()
        .setLink(Uri.parse(link))
        .setDomainUriPrefix("https://example.page.link")
        .setAndroidParameters(
                new DynamicLink.AndroidParameters.Builder("com.example.android")
                        .setMinimumVersion(125)
                        .build())
        .setIosParameters(
                new DynamicLink.IosParameters.Builder("com.example.ios")
                        .setAppStoreId("123456789")
                        .setMinimumVersion("1.0.1")
                        .build())
        .buildShortDynamicLink()
        .addOnSuccessListener(new OnSuccessListener<ShortDynamicLink>() {
            @Override
            public void onSuccess(ShortDynamicLink shortDynamicLink) {
                mInvitationUrl = shortDynamicLink.getShortLink();
                // ...
            }
        });

Kotlin+KTX

val user = Firebase.auth.currentUser!!
val uid = user.uid
val invitationLink = "https://mygame.example.com/?invitedby=$uid"
Firebase.dynamicLinks.shortLinkAsync {
    link = Uri.parse(invitationLink)
    domainUriPrefix = "https://example.page.link"
    androidParameters("com.example.android") {
        minimumVersion = 125
    }
    iosParameters("com.example.ios") {
        appStoreId = "123456789"
        minimumVersion = "1.0.1"
    }
}.addOnSuccessListener { shortDynamicLink ->
    mInvitationUrl = shortDynamicLink.shortLink
    // ...
}

Отправить приглашения

Теперь, когда вы создали ссылку, вы можете включить ее в приглашение. Приглашение может быть отправлено по электронной почте, в SMS-сообщении или любым другим способом, в зависимости от того, что наиболее подходит для вашего приложения и аудитории.

Например, чтобы отправить приглашение по электронной почте:

Быстрый

Примечание. Этот продукт Firebase недоступен для целей macOS, Mac Catalyst, tvOS или watchOS.
guard let referrerName = Auth.auth().currentUser?.displayName else { return }
let subject = "\(referrerName) wants you to play MyExampleGame!"
let invitationLink = invitationUrl?.absoluteString
let msg = "<p>Let's play MyExampleGame together! Use my <a href=\"\(invitationLink)\">referrer link</a>!</p>"

if !MFMailComposeViewController.canSendMail() {
  // Device can't send email
  return
}
let mailer = MFMailComposeViewController()
mailer.mailComposeDelegate = self
mailer.setSubject(subject)
mailer.setMessageBody(msg, isHTML: true)
myView.present(mailer, animated: true, completion: nil)

Java

String referrerName = FirebaseAuth.getInstance().getCurrentUser().getDisplayName();
String subject = String.format("%s wants you to play MyExampleGame!", referrerName);
String invitationLink = mInvitationUrl.toString();
String msg = "Let's play MyExampleGame together! Use my referrer link: "
        + invitationLink;
String msgHtml = String.format("<p>Let's play MyExampleGame together! Use my "
        + "<a href=\"%s\">referrer link</a>!</p>", invitationLink);

Intent intent = new Intent(Intent.ACTION_SENDTO);
intent.setData(Uri.parse("mailto:")); // only email apps should handle this
intent.putExtra(Intent.EXTRA_SUBJECT, subject);
intent.putExtra(Intent.EXTRA_TEXT, msg);
intent.putExtra(Intent.EXTRA_HTML_TEXT, msgHtml);
if (intent.resolveActivity(getPackageManager()) != null) {
    startActivity(intent);
}

Kotlin+KTX

val referrerName = Firebase.auth.currentUser?.displayName
val subject = String.format("%s wants you to play MyExampleGame!", referrerName)
val invitationLink = mInvitationUrl.toString()
val msg = "Let's play MyExampleGame together! Use my referrer link: $invitationLink"
val msgHtml = String.format("<p>Let's play MyExampleGame together! Use my " +
        "<a href=\"%s\">referrer link</a>!</p>", invitationLink)

val intent = Intent(Intent.ACTION_SENDTO).apply {
    data = Uri.parse("mailto:") // only email apps should handle this
    putExtra(Intent.EXTRA_SUBJECT, subject)
    putExtra(Intent.EXTRA_TEXT, msg)
    putExtra(Intent.EXTRA_HTML_TEXT, msgHtml)
}
intent.resolveActivity(packageManager)?.let {
    startActivity(intent)
}

Получить реферальную информацию в вашем приложении

Когда получатель приглашения откроет реферальную ссылку, он будет перенаправлен в App Store или Play Store для установки вашего приложения, если оно еще не установлено. Затем, когда они впервые откроют ваше приложение, вы сможете получить реферальную информацию, которую вы включили в динамическую ссылку, и использовать ее для применения вознаграждения.

Обычно вы хотите предоставлять реферальные вознаграждения только после того, как получатель приглашения зарегистрируется, или даже только после того, как новый пользователь выполнит какое-либо задание. Пока критерии вознаграждения не будут выполнены, вам необходимо отслеживать информацию о вознаграждении, которую вы получили от динамической ссылки.

Одним из способов отслеживания этой информации является анонимный вход пользователя и сохранение данных в записи базы данных реального времени анонимной учетной записи. Когда получатель регистрируется и анонимная учетная запись преобразуется в постоянную учетную запись, новая учетная запись будет иметь тот же UID, что и анонимная учетная запись, и, как следствие, будет иметь доступ к информации о вознаграждении.

Например, чтобы сохранить UID реферера после того, как получатель откроет ваше приложение:

Быстрый

Примечание. Этот продукт Firebase недоступен для целей macOS, Mac Catalyst, tvOS или watchOS.
struct MyApplication: App {

  var body: some Scene {
    WindowGroup {
      VStack {
        Text("Example text")
      }
      .onOpenURL { url in
        if DynamicLinks.dynamicLinks()?.shouldHandleDynamicLink(fromCustomSchemeURL: url) ?? false {
        let dynamicLink = DynamicLinks.dynamicLinks()?.dynamicLink(fromCustomSchemeURL: url)
        handleDynamicLink(dynamicLink)
      }
      // Handle incoming URL with other methods as necessary
      // ...
      }
    }
  }
}

func handleDynamicLink(_ dynamicLink: DynamicLink?) {
  guard let dynamicLink = dynamicLink else { return false }
  guard let deepLink = dynamicLink.url else { return false }
  let queryItems = URLComponents(url: deepLink, resolvingAgainstBaseURL: true)?.queryItems
  let invitedBy = queryItems?.filter({(item) in item.name == "invitedby"}).first?.value
  let user = Auth.auth().currentUser
  // If the user isn't signed in and the app was opened via an invitation
  // link, sign in the user anonymously and record the referrer UID in the
  // user's RTDB record.
  if user == nil && invitedBy != nil {
    Auth.auth().signInAnonymously() { (user, error) in
      if let user = user {
        let userRecord = Database.database().reference().child("users").child(user.uid)
        userRecord.child("referred_by").setValue(invitedBy)
        if dynamicLink.matchConfidence == .weak {
          // If the Dynamic Link has a weak match confidence, it is possible
          // that the current device isn't the same device on which the invitation
          // link was originally opened. The way you handle this situation
          // depends on your app, but in general, you should avoid exposing
          // personal information, such as the referrer's email address, to
          // the user.
        }
      }
    }
  }
}

Java

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // ...

    FirebaseDynamicLinks.getInstance()
            .getDynamicLink(getIntent())
            .addOnSuccessListener(this, new OnSuccessListener<PendingDynamicLinkData>() {
                @Override
                public void onSuccess(PendingDynamicLinkData pendingDynamicLinkData) {
                    // Get deep link from result (may be null if no link is found)
                    Uri deepLink = null;
                    if (pendingDynamicLinkData != null) {
                        deepLink = pendingDynamicLinkData.getLink();
                    }
                    //
                    // If the user isn't signed in and the pending Dynamic Link is
                    // an invitation, sign in the user anonymously, and record the
                    // referrer's UID.
                    //
                    FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
                    if (user == null
                            && deepLink != null
                            && deepLink.getBooleanQueryParameter("invitedby", false)) {
                        String referrerUid = deepLink.getQueryParameter("invitedby");
                        createAnonymousAccountWithReferrerInfo(referrerUid);
                    }
                }
            });
}

private void createAnonymousAccountWithReferrerInfo(final String referrerUid) {
    FirebaseAuth.getInstance()
            .signInAnonymously()
            .addOnSuccessListener(new OnSuccessListener<AuthResult>() {
                @Override
                public void onSuccess(AuthResult authResult) {
                    // Keep track of the referrer in the RTDB. Database calls
                    // will depend on the structure of your app's RTDB.
                    FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
                    DatabaseReference userRecord =
                            FirebaseDatabase.getInstance().getReference()
                                    .child("users")
                                    .child(user.getUid());
                    userRecord.child("referred_by").setValue(referrerUid);
                }
            });
}

Kotlin+KTX

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    // ...

    Firebase.dynamicLinks
            .getDynamicLink(intent)
            .addOnSuccessListener(this) { pendingDynamicLinkData ->
                // Get deep link from result (may be null if no link is found)
                var deepLink: Uri? = null
                if (pendingDynamicLinkData != null) {
                    deepLink = pendingDynamicLinkData.link
                }
                //
                // If the user isn't signed in and the pending Dynamic Link is
                // an invitation, sign in the user anonymously, and record the
                // referrer's UID.
                //
                val user = Firebase.auth.currentUser
                if (user == null &&
                        deepLink != null &&
                        deepLink.getBooleanQueryParameter("invitedby", false)) {
                    val referrerUid = deepLink.getQueryParameter("invitedby")
                    createAnonymousAccountWithReferrerInfo(referrerUid)
                }
            }
}

private fun createAnonymousAccountWithReferrerInfo(referrerUid: String?) {
    Firebase.auth
            .signInAnonymously()
            .addOnSuccessListener {
                // Keep track of the referrer in the RTDB. Database calls
                // will depend on the structure of your app's RTDB.
                val user = Firebase.auth.currentUser
                val userRecord = Firebase.database.reference
                        .child("users")
                        .child(user!!.uid)
                userRecord.child("referred_by").setValue(referrerUid)
            }
}

Затем, когда получатель приглашения решит создать учетную запись, вы можете перенести реферальную информацию из анонимной учетной записи в новую учетную запись получателя приглашения.

Во-первых, получите объект AuthCredential , используя метод входа, который хочет использовать приглашенный. Например, чтобы войти с адресом электронной почты и паролем:

Быстрый

Примечание. Этот продукт Firebase недоступен для целей macOS, Mac Catalyst, tvOS или watchOS.
let credential = EmailAuthProvider.credential(withEmail: email, password: password)

Java

AuthCredential credential = EmailAuthProvider.getCredential(email, password);

Kotlin+KTX

val credential = EmailAuthProvider.getCredential(email, password)

Затем свяжите эти учетные данные с анонимной учетной записью:

Быстрый

Примечание. Этот продукт Firebase недоступен для целей macOS, Mac Catalyst, tvOS или watchOS.
if let user = Auth.auth().currentUser {
  user.link(with: credential) { (user, error) in
    // Complete any post sign-up tasks here.
  }
}

Java

FirebaseAuth.getInstance().getCurrentUser()
        .linkWithCredential(credential)
        .addOnSuccessListener(new OnSuccessListener<AuthResult>() {
            @Override
            public void onSuccess(AuthResult authResult) {
                // Complete any post sign-up tasks here.
            }
        });

Kotlin+KTX

Firebase.auth.currentUser!!
        .linkWithCredential(credential)
        .addOnSuccessListener {
            // Complete any post sign-up tasks here.
        }

Новая постоянная учетная запись имеет доступ ко всем данным о вознаграждениях, которые вы добавили в анонимную учетную запись.

Предоставление вознаграждения рефереру и получателю

Теперь, когда вы извлекли и сохранили данные приглашения из динамической ссылки, вы можете предоставлять реферальные вознаграждения рефереру и получателю всякий раз, когда выполняются требуемые вами критерии.

Хотя вы можете писать в базу данных реального времени из своего клиентского приложения, вам часто нужно разрешить доступ только для чтения к данным, таким как валюта в приложении, из ваших приложений и выполнять операции записи только из вашего бэкэнда. Такой серверной частью может быть любая система, поддерживающая Firebase Admin SDK, но чаще всего проще всего использовать облачные функции для выполнения этих задач.

Например, предположим, что у вас есть игра, и вы хотите предоставить вознаграждение в виде внутриигровой валюты получателю после того, как получатель зарегистрируется, и отправителю после того, как получатель достигнет уровня 5.

Чтобы предоставить вознаграждение за регистрацию, разверните функцию, которая отслеживает создание определенного ключа базы данных реального времени и предоставляет вознаграждение, когда оно будет создано. Например:

const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);

exports.grantSignupReward = functions.database.ref('/users/{uid}/last_signin_at')
    .onCreate(event => {
      var uid = event.params.uid;
      admin.database().ref(`users/${uid}/referred_by`)
        .once('value').then(function(data) {
          var referred_by_somebody = data.val();
          if (referred_by_somebody) {
            var moneyRef = admin.database()
                .ref(`/users/${uid}/inventory/pieces_of_eight`);
            moneyRef.transaction(function (current_value) {
              return (current_value || 0) + 50;
            });
          }
        });
    });

Затем, когда новый пользователь зарегистрируется, активируйте эту функцию, создав ключ базы данных реального времени. Например, вызовите функцию в обработчике успеха linkWithCredential , который вы создали на предыдущем шаге:

Быстрый

Примечание. Этот продукт Firebase недоступен для целей macOS, Mac Catalyst, tvOS или watchOS.
if let user = Auth.auth().currentUser {
  user.link(with: credential) { (user, error) in
    // Complete any post sign-up tasks here.

    // Trigger the sign-up reward function by creating the "last_signin_at" field.
    // (If this is a value you want to track, you would also update this field in
    // the success listeners of your Firebase Authentication signIn calls.)
    if let user = user {
      let userRecord = Database.database().reference().child("users").child(user.uid)
      userRecord.child("last_signin_at").setValue(ServerValue.timestamp())
    }
  }
}

Java

FirebaseAuth.getInstance().getCurrentUser()
        .linkWithCredential(credential)
        .addOnSuccessListener(new OnSuccessListener<AuthResult>() {
            @Override
            public void onSuccess(AuthResult authResult) {
                // Complete any post sign-up tasks here.

                // Trigger the sign-up reward function by creating the
                // "last_signin_at" field. (If this is a value you want to track,
                // you would also update this field in the success listeners of
                // your Firebase Authentication signIn calls.)
                FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
                DatabaseReference userRecord =
                        FirebaseDatabase.getInstance().getReference()
                                .child("users")
                                .child(user.getUid());
                userRecord.child("last_signin_at").setValue(ServerValue.TIMESTAMP);
            }
        });

Kotlin+KTX

Firebase.auth.currentUser!!
        .linkWithCredential(credential)
        .addOnSuccessListener {
            // Complete any post sign-up tasks here.

            // Trigger the sign-up reward function by creating the
            // "last_signin_at" field. (If this is a value you want to track,
            // you would also update this field in the success listeners of
            // your Firebase Authentication signIn calls.)
            val user = Firebase.auth.currentUser!!
            val userRecord = Firebase.database.reference
                    .child("users")
                    .child(user.uid)
            userRecord.child("last_signin_at").setValue(ServerValue.TIMESTAMP)
        }

Чтобы предоставить рефереру вознаграждение, когда получатель достигнет уровня 5, разверните функцию, которая отслеживает изменения в поле level в ваших пользовательских записях. Если пользователь перешел с уровня 4 на уровень 5, и у пользователя есть зарегистрированный реферер, выдайте вознаграждение:

const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);

exports.rewardReferrals = functions.database.ref('/users/{uid}/level')
    .onUpdate(event => {
      var level = event.data.val();
      var prev_level = event.data.previous.val();
      if (prev_level == 4 && level == 5) {
        var referrerRef = event.data.ref.parent.child('referred_by');
        return referrerRef.once('value').then(function(data) {
          var referrerUid = data.val();
          if (referrerUid) {
            var moneyRef = admin.database()
                .ref(`/users/${referrerUid}/inventory/pieces_of_eight`);
            return moneyRef.transaction(function (current_value) {
              return (current_value || 0) + 50;
            });
          }
        });
      }
    });

И реферер, и ваш новый пользователь теперь получили свои вознаграждения.