React-native-iap: تحقق IOS مما إذا كان اشتراك التجديد التلقائي لا يزال نشطًا

تم إنشاؤها على ٢٧ سبتمبر ٢٠١٨  ·  61تعليقات  ·  مصدر: dooboolab/react-native-iap

نسخة من رد فعل - أصلية - IAP

2.2.2

المنصات التي واجهت الخطأ فيها (IOS أو Android أو كليهما؟)

IOS

سلوك متوقع

باستخدام RNIAP api ، يمكننا التحقق مما إذا كان اشتراك التجديد التلقائي لا يزال نشطًا

السلوك الفعلي

على android ، أفعل ذلك للتحقق مما يلي:

export const isUserSubscriptionActive = async (subscriptionId) =>{
    // Get all the items that the user has
    const availablePurchases = await getAvailablePurchases();
    if(availablePurchases !== null && availablePurchases.length > 0){
        const subscription = availablePurchases.find((element)=>{
            return subscriptionId === element.productId;
        });
        if(subscription){
             // check for the autoRenewingAndroid flag. If it is false the sub period is over
              return subscription["autoRenewingAndroid"] == true;
            }
        }else{
            return false;
        }
    }
}

على نظام iOS ، لا توجد علامة للتحقق من ذلك ، وتعيد طريقة getAvailablePurchases جميع عمليات الشراء التي تم إجراؤها ، حتى الاشتراكات غير النشطة في الوقت الحالي.

هل هناك طريقة للتحقق من هذا؟

يعتبر،
ماركوس

📱 iOS 🚶🏻 stale

التعليق الأكثر فائدة

فيما يلي الوظيفة التي أستخدمها والتي تعمل على كل من Android و iOS ، حيث تقوم بالفرز بشكل صحيح على نظام iOS لضمان حصولنا على أحدث بيانات الإيصال:

import * as RNIap from 'react-native-iap';
import {ITUNES_CONNECT_SHARED_SECRET} from 'react-native-dotenv';

const SUBSCRIPTIONS = {
  // This is an example, we actually have this forked by iOS / Android environments
  ALL: ['monthlySubscriptionId', 'yearlySubscriptionId'],
}

async function isSubscriptionActive() {
  if (Platform.OS === 'ios') {
    const availablePurchases = await RNIap.getAvailablePurchases();
    const sortedAvailablePurchases = availablePurchases.sort(
      (a, b) => b.transactionDate - a.transactionDate
    );
    const latestAvailableReceipt = sortedAvailablePurchases[0].transactionReceipt;

    const isTestEnvironment = __DEV__;
    const decodedReceipt = await RNIap.validateReceiptIos(
      {
        'receipt-data': latestAvailableReceipt,
        password: ITUNES_CONNECT_SHARED_SECRET,
      },
      isTestEnvironment
    );
    const {latest_receipt_info: latestReceiptInfo} = decodedReceipt;
    const isSubValid = !!latestReceiptInfo.find(receipt => {
      const expirationInMilliseconds = Number(receipt.expires_date_ms);
      const nowInMilliseconds = Date.now();
      return expirationInMilliseconds > nowInMilliseconds;
    });
    return isSubValid;
  }

  if (Platform.OS === 'android') {
    // When an active subscription expires, it does not show up in
    // available purchases anymore, therefore we can use the length
    // of the availablePurchases array to determine whether or not
    // they have an active subscription.
    const availablePurchases = await RNIap.getAvailablePurchases();

    for (let i = 0; i < availablePurchases.length; i++) {
      if (SUBSCRIPTIONS.ALL.includes(availablePurchases[i].productId)) {
        return true;
      }
    }
    return false;
  }
} 

ال 61 كومينتر

أنا أعمل على اكتشاف هذا أيضًا. لم أختبرها بعد ، ولكن مما قرأته أجمع أنه من الممكن إنشاء "سر مشترك" في iTunes Connect وتمرير هذا للتحقق من صحة RececeiptIos باستخدام مفتاح "كلمة المرور". أعتقد أن هذا سيعيد بعد ذلك كائن JSON الذي سيحتوي على رمز يشير إلى حالة التحقق من صحة الاشتراك والمفاتيح latest_receipt و latest_receipt_info و latest_expired_receipt info ، من بين أشياء أخرى ، والتي يمكنك استخدامها لتحديد حالة الاشتراك. أنا حرفيًا فقط أكتشف هذا ، لذا لم أختبره بعد. هذا ما أقوم بتجميعه من المشكلات ومستندات Apple. إذا نجح هذا الأمر ، فيجب أن يكون واضحًا جدًا في الوثائق بدلاً من أن يكون مدفونًا في المشكلات.
أعتقد أن الروابط التالية ذات صلة:
https://developer.apple.com/library/archive/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateRemotely.html

203 # 237

تحرير : أستطيع أن أؤكد أن العملية التي ذكرتها أعلاه تعمل. أعتقد أنه يجب علينا العمل للحصول على شرح كامل لهذا في المستندات. سأنفذ شيئًا كهذا عند تشغيل التطبيق لتحديد ما إذا كان الاشتراك قد انتهى أم لا:

RNIap.getPurchaseHistory()
        .then(purchases => {
                RNIap.validateReceiptIos({
                   //Get receipt for the latest purchase
                    'receipt-data': purchases[purchases.length - 1].transactionReceipt,
                    'password': 'whateveryourpasswordis'
                }, __DEV__)
                    .then(receipt => {
                       //latest_receipt_info returns an array of objects each representing a renewal of the most 
                       //recently purchased item. Kinda confusing terminology
                        const renewalHistory = receipt.latest_receipt_info
                       //This returns the expiration date of the latest renewal of the latest purchase
                        const expiration = renewalHistory[renewalHistory.length - 1].expires_date_ms
                       //Boolean for whether it has expired. Can use in your app to enable/disable subscription
                        console.log(expiration > Date.now())
                    })
                    .catch(error => console.log(`Error`))
        })

تضمين التغريدة

متألق!

فقط بعض الأشياء التي ليست تافهة جدًا في التعليمات البرمجية الخاصة بك ؛

  1. يجب أن يكون

const expiration = renalHistory [RenalHistory.length - 1] .expires_date_ms

بدلا من (ربما مجرد خطأ مطبعي)

تاريخ انتهاء الصلاحية = latestRenewalReceipt [latestRenewal.length - 1] .expires_date_ms

2) بالنسبة لأولئك الذين لا يعرفون ما هي "كلمة المرور": "whateveryourpasswordis":

يمكنك إنشاء مفتاح السر المشترك الخاص بك لرسائل In App واستخدامه في تطبيقك

الخطوات هنا: https://www.appypie.com/faqs/how-can-i-get-shared-secret-key-for-in-app-purchase

+1 لتحديث المستندات ، أيضًا ، ربما dooboolab يمكنك فصل حالات الاستخدام في IOS ويبدو أن Android هناك بعض الأشياء المختلفة. يمكنني مساعدتك إذا كنت تريد ذلك.

يعتبر

تضمين التغريدة

ماذا يحدث لاشتراكات التجديد التلقائي؟

يبدو أن expires_date_ms هو تاريخ انتهاء الصلاحية الأول ، ولم يتم تحديثه

لقد اختبرت هذا:

  1. اشترك في عنصر الاشتراك
  2. تحقق من تاريخ انتهاء الصلاحية (مثل 5 دقائق بعد)
  3. إنتظر 10 دقائق
  4. الاشتراك لا يزال نشطًا يبدو أنه اشتراك التجديد التلقائي
  5. إذا تحققت من أن تاريخ انتهاء صلاحية إيصال التجديد الأخير لا يزال هو نفسه النقطة 2

أيه أفكار؟

سيئتي ، يبدو أن تاريخ انتهاء الصلاحية قد تم تحديثه ، الأمر يستغرق بعض الوقت فقط.

سأترك هذا التعليق هنا في حالة وجود أي شخص في نفس السيناريو.

يعتبر

  1. يجب أن يكون

const expiration = renalHistory [RenalHistory.length - 1] .expires_date_ms

بدلا من (ربما مجرد خطأ مطبعي)

تاريخ انتهاء الصلاحية = latestRenewalReceipt [latestRenewal.length - 1] .expires_date_ms

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

ولم يكن لدي هذا السيناريو الدقيق مع تاريخ انتهاء الصلاحية ولكن كان هناك بعض الأشياء التي لم يتم تجديدها تلقائيًا على الإطلاق ، على الرغم من أنه تم حلها بطريقة ما من تلقاء نفسها (وهو ما يقلقني ، لكن لا يمكنني إعادة إنتاج ذلك أتساءل ماذا أفعل). المشكلة التي كانت لديك هي مشكلة شائعة إلى حد ما. إنه سلوك متوقع من طرف Apple ، لذا يجب ترك فترة تخزين مؤقت لتجديد الاشتراك قبل إلغاء اشتراك المستخدم. تشرح هذه المشكلة في SO بمزيد من التفصيل: https://stackoverflow.com/questions/42158460/autorenewable-subscription-iap-renewing-after-expiry-date-in-sandbox

شكرا! معلومات عظيمة ، يمكن أن يكون السبب

حول هذه المشكلة ، أعتقد أن كود kevinEsherick يجب أن يكون على المستندات 👍 إنها طريقة رائعة للتحقق من الاشتراكات!

نعم. سأكون ممتنًا لو حصلت على مستند أنيق في readme إذا طلب أي شخص منكم PR . شكرا.

أهلا يا أصدقاء. لقد أضفت قسم الأسئلة والأجوبة في الملف التمهيدي. آمل أن يتمكن أي شخص منكم من إعطائنا PR لهذه المشكلة.

سأبحث في إقامة علاقات عامة في الأيام القليلة القادمة :)

أنا أتطلع أيضًا لاستخدام هذه المكتبة خصيصًا للاشتراكات. كل من iOS و Android. سيكون توثيق الطريقة الصحيحة لتحديد ما إذا كان المستخدم لديه اشتراك صالح موضع تقدير كبير. 😄

تطبيقcuriousdustin Apple على هذا أمر مروع حقًا إذا المتوسط . نظرًا لأنك بحاجة إلى التعامل مع هذا النوع من الأشياء في الخلفية الخاصة بك ، لم نتمكن من دعم هذا بشكل كامل في وحدتنا. ومع ذلك ، يمكنك الحصول على availablePurchases في android باستخدام طريقتنا getAvailablePurchases والتي لن تعمل على منتج اشتراك ios.

إذن هل تقول أن الحل الذي يعمل عليه @ marcosmartinez7 و kevinEsherick في هذا الموضوع ليس حلاً ، وأن خادمًا خلفيًا بخلاف Apple ضروري لتأكيد حالة اشتراك iOS؟

من خلال قراءة مقال Medium الذي نشرته مع مستندات Apple ، يبدو أن استخدام الخادم هو المفضل ، ولكن ليس من المستحيل تحديد حالة الاشتراك مع الجهاز فقط.

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

ومع ذلك ، ليس من المستحيل الاستمرار في المحاولة باستخدام react-native-iap نظرًا لأننا نقدم الجلب المباشر لإيصال التحقق من صحة خادم Apple (وضع الحماية والإنتاج) والذي لحسن الحظ عمل @ marcosmartinez7 و kevinEsherick .

أعتقد أن هذا هو الحل حاليًا للعمل على التحقق من اشتراك ios .

مرحبًا يا رفاق ، آسف لقد مر أكثر من بضعة أيام ، لقد انخرطت في العمل لكنني ما زلت سأرسل تلك العلاقات العامة لتحديث README. curiousdustin الطريقة التي كتبتها أعلاه تعمل بالتأكيد وسأضيف ذلك إلى المستندات. سأقدم PR الليلة أو غدًا :)

شكرا للتحديث.

شيء آخر لاحظته من مثالك.

"بيانات الاستلام": المشتريات [المشتريات.الطول - 1].
...
const expiration = renalHistory [RenalHistory.length - 1] .expires_date_ms

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

كذلك هنا. عمليات الشراء والتجديد لم يتم طلب السجل بالتأكيد ، لذلك يتعين علينا الفرز أولاً. في dev ، ينتهي الأمر بالعديد من التكرارات.

فيما يلي الوظيفة التي أستخدمها والتي تعمل على كل من Android و iOS ، حيث تقوم بالفرز بشكل صحيح على نظام iOS لضمان حصولنا على أحدث بيانات الإيصال:

import * as RNIap from 'react-native-iap';
import {ITUNES_CONNECT_SHARED_SECRET} from 'react-native-dotenv';

const SUBSCRIPTIONS = {
  // This is an example, we actually have this forked by iOS / Android environments
  ALL: ['monthlySubscriptionId', 'yearlySubscriptionId'],
}

async function isSubscriptionActive() {
  if (Platform.OS === 'ios') {
    const availablePurchases = await RNIap.getAvailablePurchases();
    const sortedAvailablePurchases = availablePurchases.sort(
      (a, b) => b.transactionDate - a.transactionDate
    );
    const latestAvailableReceipt = sortedAvailablePurchases[0].transactionReceipt;

    const isTestEnvironment = __DEV__;
    const decodedReceipt = await RNIap.validateReceiptIos(
      {
        'receipt-data': latestAvailableReceipt,
        password: ITUNES_CONNECT_SHARED_SECRET,
      },
      isTestEnvironment
    );
    const {latest_receipt_info: latestReceiptInfo} = decodedReceipt;
    const isSubValid = !!latestReceiptInfo.find(receipt => {
      const expirationInMilliseconds = Number(receipt.expires_date_ms);
      const nowInMilliseconds = Date.now();
      return expirationInMilliseconds > nowInMilliseconds;
    });
    return isSubValid;
  }

  if (Platform.OS === 'android') {
    // When an active subscription expires, it does not show up in
    // available purchases anymore, therefore we can use the length
    // of the availablePurchases array to determine whether or not
    // they have an active subscription.
    const availablePurchases = await RNIap.getAvailablePurchases();

    for (let i = 0; i < availablePurchases.length; i++) {
      if (SUBSCRIPTIONS.ALL.includes(availablePurchases[i].productId)) {
        return true;
      }
    }
    return false;
  }
} 

تضمين التغريدة

في سيناريو android ، إذا كان الاشتراك متجددًا تلقائيًا ، فسيتم إعادته عند عمليات الشراء المتاحة

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

kevinEsherick شكرا! في حالتي ، لم يتم طلب الشراء ولا التجديد.

kevinEsherickandrewzey يا رفاق يمكن أن تعطي PR على هذا الحل؟ نود أن نتشارك مع الآخرين الذين يعانون.

بالتأكيد سأقوم بإعداده مباشرة بعد الانتهاء من إطلاق تطبيقنا العام =).

كنت أنتظر المزيد من المحادثة بخصوص تعليقي الأخير. لقد طرحت بعض القضايا التي لم يتم حلها بعد. من فضلك انظر أعلاه للرجوع اليها. بمجرد أن ننتهي من ذلك ، يمكن لأحدنا أن يقيم علاقات عامة.

kevinEsherick آه آسف ، فاتني ذلك.

expires_date_ms فريدًا بالتأكيد بالنسبة لي في كل إيصال تجديد. ربما أسأت الفهم؟ لقد لاحظت أن مصفوفة إيصالات التجديدات المرفقة بكل إيصال تم فك ترميزه هي نفسها بغض النظر عن الإيصال المحدد ، لذلك يمكنني أن أرى أنك قد تكون على صواب فيما يلي (من المثال) غير المطلوب:

const sortedAvailablePurchases = availablePurchases.sort(
      (a, b) => b.transactionDate - a.transactionDate
    );

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

أي ETA على العلاقات العامة؟ أفكر في الانتقال من هذه الحزمة لأنها لا تتعامل مع هذه الحالة على الإطلاق.

schumannd نظرًا لأن PR سيكون فقط لتحديث المستندات ، يمكنك الانتقال إلى react-native-iap الآن.

لقد أكدت أن مقتطف الشفرة الخاص بي يعمل في الواقع دون مشكلة في الإنتاج مع تطبيقنا الذي تم إطلاقه مؤخرًا: https://itunes.apple.com/us/app/rp-diet/id1330041267؟ls=1&mt=8

تحرير ، سأقوم بالتأكيد بإدخال العلاقات العامة (لديّ ذلك في قائمة "رد الجميل إلى OSS" في Trello) ، ولكن بعد جنون عطلة نهاية الأسبوع للجمعة السوداء 😄

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

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

لقد كنت أبحث عن الطريقة الصحيحة لإدارة اشتراكات التجديد التلقائي في React Native لأسابيع دون جدوى ، لذلك قمت بتبديل التروس وبدأت في البحث عن كيفية إدارتها محليًا في Objective-C / Swift. أعطاني هذا نقطة مرجعية لمقارنة واجهة (واجهات) StoreKit الأصلية والاستخدام الموثق بما تم تنفيذه في هذه المكتبة.

هذه الكتابة هي أفضل تفسير وجدته حتى الآن حول كيفية معالجة شراء الاشتراك والتحقق من صحته واستعادته من خلال تطبيق iOS أصلي: https://www.raywenderlich.com/659-in-app-purchases-auto -تجديد-الاشتراكات-البرنامج التعليمي وبينما يتم استخدام جميع واجهات برمجة التطبيقات في react-native-iap ، فإنهم يقومون بتنفيذ سير عمل مختلف.

الفرق الرئيسي الذي أراه هو استخدام appStoreReceiptUrl .

يوفر StoreKit appStoreReceiptUrl والذي ، حسب فهمي ، هو الطريقة الصحيحة لتحميل معلومات الشراء / الإيصال الحالية لتطبيق iOS. بينما أرى أن هذا يتم استخدامه والإشارة إليه في رمز react-native-iap ، يبدو أنه في سياق مختلف عن سير العمل الموثق في المقالة المرجعية.

لست متأكدًا مما إذا كان اختلاف التنفيذ هذا مشكلة ، ولكن يبدو أن واجهة برمجة التطبيقات react-native-iap تنشئ تبعية غير ضرورية على StoreKit restoreCompletedTransactions وهو ما يُطلق عليه في النهاية getPurchaseHistory .

يبدو لي أن API react-native-iap يجب أن يدعم سير العمل التالي لنظام iOS:

  • محاولة تحميل إيصال التطبيق الحالي (عبر appStoreReceiptUrl ) عند بدء تشغيل التطبيق
  • وفر فرصة للتحقق من صحة الإيصال
  • إذا كان appStoreReceiptUrl فارغًا ، فابدأ "استعادة" على الجهاز الحالي الذي سيعين بعد ذلك قيمة appStoreReceiptUrl ويمكن إعادة محاولة منطق التحقق

افكار؟

مرة أخرى ، قد يكون لدي سوء فهم لتنفيذ هذه المكتبة أو التنفيذ الأصلي المشار إليه.

تضمين التغريدة

أعتقد أن النقطة الأساسية التي تم ذكرها أيضًا في المقالة التي ربطتها هي:

ملاحظة: يقوم تطبيق المشروع بتشغيل SelfieService ، والتي تقبل الإيصالات وتحميلها مباشرة إلى خدمة التحقق من صحة الإيصالات من Apple. هذا "غش" للحفاظ على تركيز البرنامج التعليمي على إعداد الاشتراكات. في التطبيق الحقيقي ، يجب عليك استخدام خادم بعيد لهذا - وليس التطبيق.

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

  • إرسال جميع بيانات المعاملات على الفور إلى الواجهة الخلفية الخاصة بك
  • توفير بعض وسائل إعادة الإرسال في حالة حدوث مشاكل في الإرسال (مثل استعادة المشتريات)
  • بشكل دوري (على سبيل المثال مرة واحدة في اليوم) تحقق من جميع الإيصالات المخزنة على الواجهة الخلفية الخاصة بك
  • استعلامات التطبيق الخلفية لحالة الاشتراك على سبيل المثال العودة من الخلفية.

بدا لي أن هذه الطريقة الوحيدة المعقولة للتعامل مع الاشتراكات. لقد تحققت أيضًا من هذه العملية مع مطورين آخرين يستخدمون اشتراكات قابلة للتجديد التلقائي تجاريًا لبعض الوقت.

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

تضمين التغريدة

لست متأكدًا من أننا نفهم أو نتحدث عن نفس المشكلة.

يجب أن يتم التحقق من صحة الإيصال على الخادم. وفقًا لوثائق Apple الخاصة :

لا تتصل بنقطة نهاية App Store بخادم متجر التطبيقات /verifyReceipt من تطبيقك.

فسرت "الغش" في المقالة المشار إليها على أنها مكالمات مباشرة من التطبيق النموذجية إلى /verifyReceipt مباشرة من التطبيق للإيجاز بدلاً من التفويض إلى خادم خلفي.

يبدو لي أن Apple / App Store هو المصدر الوحيد للحقيقة ويجب أن يكون كذلك. تقوم Apple في النهاية بمعالجة عمليات الشراء والتجديدات والإلغاءات وما إلى ذلك وإنشاء الإيصالات وفقًا لذلك وإتاحة هذه البيانات عبر واجهات برمجة التطبيقات StoreKit .

يتعامل react-native-iap مع عمليات الشراء واستعادة المشتريات دون مشكلة ، ولكن ما زلت أعتقد أن هناك فجوة في الوصول إلى بيانات إيصال متجر التطبيقات التي يتم توفيرها محليًا بسعر appStoreReceiptUrl وهو مرة أخرى طريقة Apple الموثقة للوصول إلى الإيصال البيانات :

لاسترداد بيانات الإيصال ، استخدم طريقة appStoreReceiptURL NSBundle لتحديد موقع إيصال التطبيق ...

سير العمل الذي أفهمه من قراءة مستندات Apple والإشارة إلى تطبيقات Object-C / Swift الأصلية هو كما يلي:

  • تحميل المنتجات للشراء
  • شراء منتج (مستهلك ، غير قابل للاستهلاك ، اشتراك ، لا يهم)
  • يتم تخزين إيصال الشراء محليًا بسعر appStoreReceiptUrl بواسطة StoreKit خلف الكواليس
  • يجب أن يرسل التطبيق بيانات الإيصال إلى خادم خلفية (وليس متجر التطبيقات /verifyReceipt مباشرة) للتحقق من الصحة حسب الحاجة
  • يتم تحديث appStoreReceiptUrl بواسطة StoreKit كلما حدثت الإلغاءات و / أو التجديدات (بافتراض أن المفوضين المناسبين StoreKit مسجلون بشكل صحيح)
  • عندما يكون appStoreReceiptUrl فارغًا ، لأن المستخدم على جهاز جديد ، وما إلى ذلك ، استخدم سير عمل استعادة الشراء الذي سيسترد بيانات الإيصال الحالية من Apple / AppStore و StoreKit سيستمر محليًا عند appStoreReceiptUrl خلف الكواليس

مرة أخرى ، يعالج react-native-iap كل هذا باستثناء تحميل بيانات الإيصال من appStoreReceiptUrl . أعتقد أن طريقة getReceipt التي استفسرت عن الإيصال المحلي ستخفف من عبء إدارة الاشتراكات و / أو أي مشتريات أخرى.

جعلت شركة Apple هذا التفكير أكثر صعوبة مما ينبغي.

يتم تحديث appStoreReceiptUrl بواسطة StoreKit كلما حدثت الإلغاءات و / أو التجديدات (بافتراض أن مفوضي StoreKit المناسبين مسجلين بشكل صحيح)

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

بخلاف ذلك ، يبدو أنه بديل جيد للنهج الذي وصفته. أتساءل أيهما أكثر استخدامًا لتطبيقات الإنتاج.

sellmeadog أعتقد أن هذه هي نفس الفكرة التي نشرتها هنا: # 356
يُعد التحقق من صحة الإيصالات محليًا إحدى الطرق ، كما توصي Apple. هذا مستقل تمامًا عن أي طلب خادم / شبكة.

نعم. هناك جوانب سلبية بخصوص "لا يوجد اتصال بالشبكة - لا يوجد تحقق من الصحة".
لكن هناك حالات استخدام لذلك.
أريد تحققًا سريعًا من الإيصال عند كل تشغيل للتطبيق ولا أريد متابعة عمليات الشراء داخل التطبيق بنفسي عبر UserDefaults أو Keychain . StoreKit آلية تخزين بالفعل.

كيف يفترض أن تتحقق مما إذا كان مستخدم IOS قد جدد أو ألغى اشتراكه دون مطالبتهم بتسجيل الدخول إلى حساب itunes عبر شيء مثل getAvailablePurchases؟

فمثلا:
عند أول شراء للاشتراك ، نتحقق من صحة الإيصال ونحفظ تاريخ انتهاء الصلاحية في ملف تعريف Firebase الخاص بهم. نستخدم عقدة Firebase هذه للتحقق من بدء التشغيل إذا كان لديهم اشتراك نشط. تأتي المشكلة عند انقضاء تاريخ انتهاء الصلاحية هذا وبدء فترة الفوترة الجديدة. كيف يمكننا التحقق لمعرفة ما إذا تم تجديدها تلقائيًا أو إلغاؤها؟ كما ذكرنا ، كنا نستخدم getAvailablePurchases ، لكن هذا يطالب بتسجيل الدخول إلى itunes مثل زر الاستعادة - وهو واجهة مستخدم رهيبة

أنا أيضا أتساءل نفس الشيء.

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

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

هل يمكننا الحصول على مزيد من المعلومات حول هذا؟

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

عذرًا ، لم أقم بتأكيد أن getAvailablePurchases() يتسبب في مطالبات كلمة المرور في الإنتاج.

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

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

curiousdustin نعم ، لذلك عندما أقوم
تحرير: يمكنني أن أؤكد أن هذا يحدث على الأجهزة التي لا تعمل بنظام الحماية. هذه تجربة مستخدم رهيبة ومشكلة كبيرة. أي شخص ، أي أفكار؟

لذلك عندما تقوم بالتحقق من صحة على الخادم الخاص بك عن طريق الاتصال بنقطة نهاية App Store / checkReceipt من تطبيقك ، فإنه سيعيد دائمًا آخر تحديث لمعلومات التاريخ لهذا المستخدم مع إيصال الشراء الأصلي الأول فقط.

هل يعرف أي شخص ما إذا كان استخدام طريقة التحقق المحلية سيعيد دائمًا أحدث المعلومات؟ أم أنه من أجل هذا الإيصال الذي تقوم بالتحقق منه محليًا؟

أيضًا ، هل لدى أي شخص نصيحة لإعداد الخادم الخاص بك للتحقق من صحة مع Apple؟ ليس لدي خبرة في ذلك

csumrell ، يمكنك الحصول على إيصال آخر عملية شراء عن طريق الاتصال بـ getPurchaseHistory وترتيبها. التعليقات أعلاه بالتفصيل كيفية القيام بذلك. يجب أن يحتوي هذا على أحدث المعلومات.

وأنا على وشك استكشاف خيار التحقق على الخادم حتى نتمكن من مساعدة بعضنا البعض. أحب أن أسمع كيف ذهب أي شخص آخر حول ذلك بالرغم من ذلك.

kevinEsherick نعم ولكن getPurchaseHistory سيؤدي إلى ظهور نافذة تسجيل الدخول المنبثقة في Itunes. أنا أتحدث عن حفظ الإيصال الأصلي في وحدة التخزين المحلية عندما يشتري المستخدم لأول مرة. ثم بدلاً من استدعاء getPurchaseHistory للتحقق من الفواتير التالية ، قم باستدعاء ValidateReceiptIos (originalReceipt) الذي تم التحقق من صحته محليًا. ومع ذلك ، لست متأكدًا مما إذا كانت ValidateReceiptIos تُرجع المعاملات المحدّثة مثل خادم Apple / checkReceipt سيفعل

تضمين التغريدة حسنًا ، هذا قابل للاختبار بسرعة وسهولة. هل لم تحاول ذلك؟ إذا لم يكن الأمر كذلك ، فسوف أتحقق من ذلك لاحقًا اليوم.
تحرير: csumrell validateReceiptIos

قامت hyochan بتمويل 20.00 دولارًا لهذه المشكلة. شاهده على IssueHunt

hyochan ما هو الحل المحدد الذي

تضمين التغريدة

هل تمول شخصًا ما لتجميع هذه الحلول في علاقات عامة واحدة موجزة إلى README؟

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

kevinEsherick أنت على صواب ، فإن التحقق المحلي باستخدام

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

ملاحظة لأولئك الذين يفكرون في استخدام تدفق التحقق هذا الذي أوضحناه أنا و

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

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

ربما يحتاج هذا التدفق برمته إلى توضيح في الوثائق؟

خاصة عندما يتعلق الأمر باشتراك التجديد التلقائي؟ من غير الواضح تمامًا للوافد الجديد ما إذا كنت قادرًا على التحقق من expires_date_ms لمعرفة ما إذا كان الاشتراك لا يزال نشطًا أم لا؟

أقترح أن أفضل مكان لهذا سيكون في مثال عملي أكثر اكتمالاً؟

هل سيكون الناس مهتمين بهذا؟ إذا حصلت على بعض الوقت ، يمكنني العمل على هذا؟

alexpchin نعم هذا سيوفر بالتأكيد الكثير من وقت نظافة الكريستال

يوافق! هل سيكون أي شخص مهتمًا بـ # 856؟

مرحبا،

عملت طريقة

نحن نشغل RN 0.61.4 و RNIap: 4.4.2 و iOS 13.3.1

لقد حاولنا:

  • إعادة تثبيت التطبيق
  • حساب مستخدم جديد
  • استخدام سر جديد للتطبيق
  • اختبار جميع الإيصالات لكل مستخدم - لا يمكن تحليل إيصال واحد

نتساءل عما إذا لم نكن نستخدم finishTransactionIOS بشكل صحيح عندما قمنا بشراء sandbox؟

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

  isSubscriptionActive = async () => {
      const availablePurchases = await RNIap.getAvailablePurchases();
      const sortedAvailablePurchases = availablePurchases.sort(
        (a, b) => b.transactionDate - a.transactionDate
      );
      const latestAvailableReceipt = sortedAvailablePurchases[0].transactionReceipt;
      const isTestEnvironment = __DEV__;

      try {
        const decodedReceipt = await RNIap.validateReceiptIos(
          {
            'receipt-data': latestAvailableReceipt,
            password: Config.IN_APP_PURCHASE_SECRET,
          },
          isTestEnvironment
        );
        console.log('response!', decodedReceipt)
      } catch (error) {
        console.warn('Error validating receipt', error) // JSON PARSE ERROR HERE
      }
...

سؤال بخصوص كود andrewzey وضع. ألن يكون Date.now() هو الوقت الحالي للجهاز ويمكن للمستخدم تغيير هذا ولديه اشتراك لا نهاية له؟ 🤔

أيضًا ، أليس التحقق من الإيصال من التطبيق نفسه أمرًا غير محبط؟

doteric هل يمكن أن تدلني إلى حيث يذكرون أنه
أنا أجد صعوبة في العثور على التوثيق الصحيح من Apple أو من مصادر أصلية قوية أخرى.

doteric yes Date.now () هو وقت الجهاز حتى يمكن حلها. ومع ذلك ، فإن فرص قيام المستخدم بذلك ضئيلة ، وحتى الطرق الأخرى يمكن حلها للحصول على اشتراك لا نهاية له. على سبيل المثال ، في حالة استخدام عملية تحقق بسيطة من جانب الخادم ، يمكنهم الدخول في وضع الطائرة قبل فتح التطبيق لمنع التطبيق من إدراك انتهاء صلاحية الاشتراك. بالطبع هناك وسائل حماية أخرى يمكنك وضعها ، لكن وجهة نظري هي أن جانب العميل الذي يستخدم Date.now () يعمل بالتأكيد. captaincole ليس لدي المستندات في متناول اليد ولكن يمكنني أن أؤكد أنني قرأت أيضًا أن التحقق من جانب العميل غير مستحسن. أقرأه في مستندات Apple على ما أعتقد. ومع ذلك ، أعتقد أن جانب العميل ينجز المهمة.

مرحبا،

كنت أحاول أحد الأساليب التي تمت مناقشتها في هذا الموضوع ، وذلك باستخدام ValidateReceiptIos وأحدث_receipt_data (مقارنة expires_date_ms بالتاريخ الفعلي). عملت بشكل رائع أثناء التطوير في Xcode. ومع ذلك ، عندما اختبرته في Testflight ، لم يعد يعمل. من الصعب تصحيح الأخطاء ، لذا لا يمكنني تحديد المشكلة بالضبط ، يبدو أنها لا تحصل على بيانات الإيصال. هل لدى أي شخص مشكلة مماثلة مع رحلة تجريبية؟

شكرا لك مقدما!

مرحبا،

كنت أحاول أحد الأساليب التي تمت مناقشتها في هذا الموضوع ، وذلك باستخدام ValidateReceiptIos وأحدث_receipt_data (مقارنة expires_date_ms بالتاريخ الفعلي). عملت بشكل رائع أثناء التطوير في Xcode. ومع ذلك ، عندما اختبرته في Testflight ، لم يعد يعمل. من الصعب تصحيح الأخطاء ، لذا لا يمكنني تحديد المشكلة بالضبط ، يبدو أنها لا تحصل على بيانات الإيصال. هل لدى أي شخص مشكلة مماثلة مع رحلة تجريبية؟

شكرا لك مقدما!

+1

asobralr @ Somnus007 قد أكون مخطئًا ، لكن لا أعتقد أنه يمكنك اختبار IAPs باستخدام testflight نظرًا لأن المستخدمين لا يمكنهم إجراء عمليات شراء من خلال testflight. لا توفر Apple فرصًا رائعة لاختبار عمليات الشراء في بيئات شبيهة بالإنتاج

asobralr @ Somnus007 قد أكون مخطئًا ، لكن لا أعتقد أنه يمكنك اختبار IAPs باستخدام testflight نظرًا لأن المستخدمين لا يمكنهم إجراء عمليات شراء من خلال testflight. لا توفر Apple فرصًا رائعة لاختبار عمليات الشراء في بيئات شبيهة بالإنتاج

صيح. لا يمكنك حتى محاكاة سيناريوهات الفشل في وضع الحماية ، وهذا عار. 😞

مرحبًا kevinEsherick ، شكرًا على ردك. قد تكون مخطئا. يمكن للمستخدم إجراء عملية شراء من خلال teslflight. في testflight ، يبدو أنه يتعين على المستخدم تسجيل الدخول إلى حساب sandbox له نفس عنوان البريد الإلكتروني مع حساب Apple الحقيقي الخاص به. ثم كل شيء يعمل بشكل جيد. راجع للشغل ، هل سيؤدي getPurchaseHistory إلى تشغيل iTunes في بيئة الإنتاج؟

يبدو أن ios 14 يكسر هذا الحل نظرًا لوجود مشكلات عند الاتصال بـ getAvailablePurchases () قبل استدعاء requestSubscription ().

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات