Join us for Firebase Summit on November 10, 2021. Tune in to learn how Firebase can help you accelerate app development, release with confidence, and scale with ease. Register

مكافأة الإحالات

واحدة من أكثر الطرق فعالية للحصول على مستخدمين جدد هي من خلال إحالات المستخدمين. يمكنك استخدام دينامية سريعة مع قاعدة البيانات في الوقت الحقيقي و ظائف الغيمة لFirebase لتشجيع المستخدمين على دعوة أصدقائهم من خلال تقديم المكافآت في التطبيق للإحالات الناجحة في كل من الإحالات والمتلقي.

الفوائد الرئيسية

  • تسريع النمو من خلال توفير حافز للمستخدمين لدعوة أصدقائهم.
  • تعمل روابط الدعوة عبر الأنظمة الأساسية.
  • يحصل المستخدمون الجدد الذين يفتحون تطبيقك لأول مرة على تجربة التشغيل الأولى التي تخصصها لهم. على سبيل المثال ، يمكنك ربطهم تلقائيًا بالصديق الذي دعاهم.
  • اختياريًا ، يمكنك تأخير منح المكافآت حتى يكمل المستخدمون الجدد بعض المهام التمهيدية ، مثل إكمال برنامج تعليمي.

إليك كيفية البدء!

إعداد Firebase و Dynamic Links SDK

قم بإعداد مشروع Firebase جديد وتثبيت Dynamic Links SDK في تطبيقك. ( دائرة الرقابة الداخلية ، الروبوت ، C ++ ، الوحدة ). يسمح تثبيت Dynamic Links SDK لـ Firebase بتمرير البيانات حول الرابط الديناميكي إلى التطبيق ، بما في ذلك بعد تثبيت المستخدم للتطبيق. بدون SDK ، لا توجد طريقة لتوصيل مستخدم ما بعد التثبيت بنقرة التثبيت المسبق.

لإنشاء دعوة ، قم أولاً بإنشاء الرابط الذي يفتحه المستلم لقبول الدعوة. لاحقًا ، ستقوم بتضمين هذا الرابط في نص الدعوة. عندما يقوم مستلم الدعوة بتثبيت تطبيقك عن طريق فتح الرابط ، يمكنه الحصول على تجربة تشغيل مخصصة لأول مرة ، بما في ذلك تلقي مكافأة داخل التطبيق.

هذا رابط الدعوة هو الارتباط الحيوي مع link المعلمة القيمة التي تشير إلى أنه من المستخدم الموجودة لديك.

هناك العديد من الطرق التي يمكن تنسيق هذه link الحمولات المعلمة وربطها إلى التطبيق الخاص بك. واحد طريقة بسيطة لتحديد هوية حساب المستخدم الخاص بالمرسل في معلمة الاستعلام كما في المثال التالي:

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

بعد ذلك، إلى خلق دينامية سريعة مناسبة لإدراجها في دعوة، يمكنك استخدام API الارتباط الحيوي باني:

iOS (سويفت)

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
}

جافا

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
    // ...
}

إرسال الدعوات

الآن بعد أن أنشأت الرابط ، يمكنك تضمينه في دعوة. يمكن أن تكون الدعوة رسالة بريد إلكتروني أو رسالة نصية قصيرة أو أي وسيلة أخرى ، اعتمادًا على ما هو أكثر ملاءمة لتطبيقك وجمهورك.

على سبيل المثال ، لإرسال دعوة بالبريد الإلكتروني:

iOS (سويفت)

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)

جافا

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 لتثبيت تطبيقك إذا لم يكن مثبتًا بالفعل. بعد ذلك ، عندما يفتحون تطبيقك لأول مرة ، يمكنك استرداد معلومات الإحالة التي أدرجتها في الرابط الديناميكي واستخدامها لتطبيق المكافأة.

عادة ، تريد منح مكافآت الإحالة فقط بعد تسجيل مستلم الدعوة ، أو حتى بعد أن يكمل المستخدم الجديد بعض المهام. حتى يتم استيفاء معايير المكافأة ، تحتاج إلى تتبع معلومات المكافأة التي حصلت عليها من الرابط الديناميكي.

طريقة واحدة لتتبع هذه المعلومات هي تسجيل دخول المستخدم بشكل مجهول وتخزين البيانات في سجل Realtime Database الخاص بالحساب المجهول. عندما يقوم المستلم بالتسجيل ويتم تحويل الحساب المجهول إلى حساب دائم ، سيكون للحساب الجديد نفس المعرف الفريد للحساب المجهول ، ونتيجة لذلك ، سيتمكن من الوصول إلى معلومات المكافأة.

على سبيل المثال ، لحفظ UID الخاص بالمحيل بعد أن يفتح المستلم تطبيقك:

iOS (سويفت)

func application(_ app: UIApplication, open url: URL, options:
    [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
  if let isDynamicLink = DynamicLinks.dynamicLinks()?.shouldHandleDynamicLink(fromCustomSchemeURL: url),
      isDynamicLink {
    let dynamicLink = DynamicLinks.dynamicLinks()?.dynamicLink(fromCustomSchemeURL: url)
    return handleDynamicLink(dynamicLink)
  }
  // Handle incoming URL with other methods as necessary
  // ...
  return false
}

@available(iOS 8.0, *)
func application(_ application: UIApplication,
    continue userActivity: NSUserActivity,
    restorationHandler: @escaping ([Any]?) -> Void) -> Bool {
  guard let dynamicLinks = DynamicLinks.dynamicLinks() else { return false }
  let handled = dynamicLinks.handleUniversalLink(userActivity.webpageURL!) { (dynamicLink, error) in
    if (dynamicLink != nil) && !(error != nil) {
      self.handleDynamicLink(dynamicLink)
    }
  }
  if !handled {
    // Handle incoming URL with other methods as necessary
    // ...
  }
  return handled
}

func handleDynamicLink(_ dynamicLink: DynamicLink?) -> Bool {
  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.
        }
      }
    }
  }
  return true
}

جافا

@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 الكائن باستخدام علامة في طريقة المدعو يريد استخدام. على سبيل المثال ، لتسجيل الدخول بعنوان بريد إلكتروني وكلمة مرور:

iOS (سويفت)

let credential = EmailAuthProvider.credential(withEmail: email, password: password)

جافا

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

Kotlin + KTX

val credential = EmailAuthProvider.getCredential(email, password)

بعد ذلك ، اربط بيانات الاعتماد هذه بالحساب المجهول:

iOS (سويفت)

if let user = Auth.auth().currentUser {
  user.link(with: credential) { (user, error) in
    // Complete any post sign-up tasks here.
  }
}

جافا

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.
        }

يتمتع الحساب الجديد والدائم بإمكانية الوصول إلى جميع بيانات المكافأة التي أضفتها إلى الحساب المجهول.

امنح المكافآت للمحيل والمستلم

الآن بعد أن استرجعت بيانات الدعوة وحفظتها من الرابط الديناميكي ، يمكنك منح مكافآت الإحالة للمحيل والمستلم متى تم استيفاء المعايير التي تريد طلبها.

على الرغم من أنه يمكنك الكتابة إلى Realtime Database من تطبيق العميل الخاص بك ، فغالبًا ما تريد السماح فقط بالوصول للقراءة إلى البيانات مثل العملة داخل التطبيق من تطبيقاتك ، وإجراء عمليات الكتابة فقط من الواجهة الخلفية الخاصة بك. يمكن أن تكون هذه الواجهة الخلفية أي نظام قادر على تشغيل Firebase Admin SDK ، ولكن غالبًا ما يكون من الأسهل استخدام وظائف السحابة لأداء هذه المهام.

على سبيل المثال ، لنفترض أن لديك لعبة وترغب في منح مكافأة بعملة داخل اللعبة إلى المستلم بعد تسجيل المستلم ، وإلى المُحيل بعد وصول المستلم إلى المستوى 5.

لمنح مكافأة التسجيل ، انشر وظيفة تراقب إنشاء مفتاح قاعدة بيانات Realtime معين ، وتمنح المكافأة عندما تكون كذلك. على سبيل المثال:

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;
            });
          }
        });
    });

بعد ذلك ، عندما يقوم مستخدم جديد بالتسجيل ، قم بتشغيل هذه الوظيفة عن طريق إنشاء مفتاح Realtime Database. على سبيل المثال، يؤدي وظيفة في linkWithCredential الصورة النجاح المستمع الذي قمت بإنشائه في الخطوة السابقة:

iOS (سويفت)

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())
    }
  }
}

جافا

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;
            });
          }
        });
      }
    });

لقد تلقى كل من المُحيل والمستخدم الجديد مكافآتهما الآن.