React-native-iap: IOS memeriksa apakah langganan perpanjangan otomatis masih aktif

Dibuat pada 27 Sep 2018  ·  61Komentar  ·  Sumber: dooboolab/react-native-iap

Versi reaksi-asli-iap

2.2.2

Platform yang Anda hadapi kesalahannya (IOS atau Android atau keduanya?)

iOS

Perilaku yang diharapkan

Dengan menggunakan api RNIAP, kami dapat memeriksa apakah langganan pembaruan otomatis masih aktif

Perilaku sebenarnya

Di Android saya melakukan ini untuk memeriksa bahwa:

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

Di ios tidak ada tanda untuk memeriksanya, dan metode getAvailablePurchases mengembalikan semua pembelian yang dilakukan, bahkan langganan yang saat ini tidak aktif.

Apakah ada cara untuk memeriksa ini?

Salam,
Marcos

📱 iOS 🚶🏻 stale

Komentar yang paling membantu

Berikut adalah fungsi yang saya gunakan yang berfungsi di Android dan iOS, menyortir dengan benar di iOS untuk memastikan kami mendapatkan data tanda terima terbaru:

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

Semua 61 komentar

Saya sedang bekerja untuk mencari tahu ini juga. Saya belum mengujinya, tetapi dari apa yang saya baca, saya menyimpulkan bahwa itu mungkin dengan membuat "rahasia bersama" di iTunes Connect dan meneruskan ini ke validasiReceiptIos dengan kunci 'kata sandi'. Saya percaya ini kemudian akan mengembalikan objek JSON yang akan berisi kode yang menunjukkan status validasi langganan dan kunci recent_receipt, recent_receipt_info, dan recent_expired_receipt info, antara lain, yang dapat Anda gunakan untuk menentukan status langganan. Saya benar-benar hanya mencari tahu ini jadi saya belum mengujinya. Itulah yang saya kumpulkan dari masalah dan dokumen Apple. Jika ini berhasil, itu benar-benar harus dibuat sangat jelas dalam dokumentasi alih-alih terkubur dalam masalah.
Saya yakin tautan berikut relevan:
https://developer.apple.com/library/archive/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateRemotely.html

203 #237

EDIT : Saya dapat mengonfirmasi bahwa proses yang saya sebutkan di atas berfungsi. Saya pikir kita harus bekerja untuk mendapatkan penjelasan lengkap tentang ini di dokumen. Saya akan menerapkan sesuatu seperti ini pada peluncuran aplikasi untuk menentukan apakah langganan telah kedaluwarsa:

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

@kevinEsherick

Cemerlang!

Hanya beberapa hal yang tidak begitu sepele tentang kode Anda;

  1. Harus

const kedaluwarsa = pembaruanHistory [renewalHistory.length - 1].expires_date_ms

bukannya (mungkin hanya salah ketik)

const kedaluwarsa = recentRenewalReceipt [latestRenewal.length - 1].expires_date_ms

2) Bagi yang tidak tahu apa itu 'password': 'whateveryourpasswordis':

Anda dapat membuat kunci rahasia bersama untuk pembayaran Dalam Aplikasi dan menggunakannya di aplikasi Anda

Langkah-langkahnya ada di sini: https://www.appypie.com/faqs/how-can-i-get-shared-secret-key-for-in-app-purchase

+1 untuk memperbarui dokumen, juga, mungkin @dooboolab Anda dapat memisahkan kasus penggunaan di iOS dan Android tampaknya ada beberapa hal yang berbeda. Saya dapat membantu Anda jika Anda mau.

Salam

@kevinEsherick

Apa yang terjadi dengan langganan perpanjangan otomatis?

Tampaknya expired_date_ms adalah tanggal kedaluwarsa pertama, tidak diperbarui

Saya telah menguji ini:

  1. Berlangganan item langganan
  2. Periksa tanggal kedaluwarsa (seperti 5 menit setelahnya)
  3. Tunggu 10 menit
  4. Langganan yang masih aktif sepertinya adalah langganan perpanjangan otomatis
  5. Kalau saya cek kadaluarsa struk perpanjangan terakhir masih sama dengan poin 2

Ada ide?

Sial, tanggal kedaluwarsa tampaknya diperbarui, hanya perlu beberapa saat.

Saya akan meninggalkan komentar ini di sini jika ada orang yang berada dalam skenario yang sama.

Salam

  1. Harus

const kedaluwarsa = pembaruanHistory [renewalHistory.length - 1].expires_date_ms

bukannya (mungkin hanya salah ketik)

const kedaluwarsa = recentRenewalReceipt [latestRenewal.length - 1].expires_date_ms

Ups ya Anda benar, itu salah ketik. Saya telah mengubah nama untuk membuat artinya lebih jelas tetapi lupa untuk mengubah semua kemunculan. sudah saya edit, terima kasih.

Dan saya belum memiliki skenario yang tepat dengan tanggal kedaluwarsa tetapi ada beberapa hal yang tidak diperpanjang secara otomatis sama sekali, meskipun entah bagaimana diselesaikan dengan sendirinya (yang membuat saya khawatir, tetapi saya tidak dapat mereproduksi jadi tidak tahu harus berbuat apa lagi). Masalah yang Anda alami adalah masalah yang cukup umum. Ini adalah perilaku yang diharapkan di pihak Apple, jadi Anda harus meninggalkan periode buffer untuk pembaruan langganan sebelum berhenti berlangganan pengguna. Masalah pada SO ini menjelaskan lebih detail: https://stackoverflow.com/questions/42158460/autorenewable-subscription-iap-renewing-after-expiry-date-in-sandbox

Terima kasih! Info yang bagus, bisa jadi alasannya

Tentang masalah ini, saya pikir kode @kevinEsherick harus ada di dokumen 👍 Ini cara yang bagus untuk memeriksa subs!

Ya. Saya akan menghargai dokumen yang rapi di readme jika ada di antara Anda yang meminta PR . Terima kasih.

Hai kawan. Saya telah menambahkan bagian Q & A di readme. Semoga siapa pun dari Anda dapat memberi kami PR untuk masalah ini.

Saya akan membuat PR dalam beberapa hari ke depan :)

Saya juga ingin menggunakan perpustakaan ini khusus untuk langganan. Baik iOS maupun Android. Mendokumentasikan cara yang tepat untuk menentukan apakah pengguna memiliki langganan yang valid akan sangat dihargai. 😄

@curiousdustin Implementasi Apple dalam hal ini sangat buruk jika Anda merujuk ke medium . Karena Anda perlu menangani hal semacam ini di backend Anda, kami tidak dapat sepenuhnya mendukung ini di modul kami. Namun, Anda bisa mendapatkan availablePurchases di android menggunakan metode kami getAvailablePurchases yang tidak akan berfungsi pada produk langganan ios.

Jadi, apakah Anda mengatakan solusi yang @marcosmartinez7 dan @kevinEsherick kerjakan di utas ini BUKAN solusi, dan bahwa server backend selain Apple diperlukan untuk mengonfirmasi status langganan iOS?

Dari membaca artikel Medium yang Anda posting bersama dengan dokumen Apple, tampaknya menggunakan server lebih disukai, tetapi bukan tidak mungkin untuk menentukan status berlangganan dengan perangkat saja.

Maaf saya melewatkan beberapa informasi dalam tulisan saya. Saya akan mengatur ini sekarang.
Apple menyarankan untuk menyimpan receipt di server Anda sendiri untuk mencegah serangan tengah. Oleh karena itu nanti Anda bisa mengecek struk tersebut untuk mengetahui apakah struk tersebut valid atau langganan masih tersedia.

Namun, bukan tidak mungkin untuk tetap mencoba react-native-iap karena kami menyediakan pengambilan langsung tanda terima validasi ke server Apple sendiri (kotak pasir & produksi) yang untungnya @marcosmartinez7 dan @kevinEsherick berhasil.

Saya pikir saat ini adalah solusi untuk memeriksa langganan ios .

Hai guys, maaf sudah lebih dari beberapa hari, saya terjebak dalam pekerjaan tetapi saya masih akan mengirimkan PR itu untuk memperbarui README. @curiousdustin metode yang saya tulis di atas pasti berfungsi dan saya akan menambahkannya ke dokumen. Saya akan mengirimkan PR malam ini atau besok :)

Terima kasih pembaruannya.

Satu hal lagi yang saya perhatikan dari contoh Anda.

'receipt-data': pembelian[purchases.length - 1].transactionReceipt,
...
const kedaluwarsa = pembaruanHistory[renewalHistory.length - 1].expires_date_ms

Dalam pengujian saya, pembelian dan pembaruanHistory tidak harus dalam urutan tertentu. Bukankah kita perlu mengurutkannya atau sesuatu untuk memverifikasi bahwa kita menggunakan yang kita inginkan?

Sama disini. Pembelian dan perpanjanganHistory pasti tidak dipesan, jadi kita memang harus mengurutkan terlebih dahulu. Di dev, itu akhirnya menjadi banyak iterasi.

Berikut adalah fungsi yang saya gunakan yang berfungsi di Android dan iOS, menyortir dengan benar di iOS untuk memastikan kami mendapatkan data tanda terima terbaru:

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

@andrewze

Pada skenario android, jika langganan dapat diperpanjang secara otomatis, itu akan dikembalikan pada Pembelian yang tersedia

@curiousdustin @andrewzey Anda benar, pembelian tidak dipesan. pembaharuanHistory/latest_receipt_info, bagaimanapun, selalu dipesan untuk saya. Saya ingat menyadari bahwa pembelian tidak dipesan tetapi saya pikir saya telah menemukan beberapa alasan bahwa ini sebenarnya tidak menjadi perhatian. Saya akan memeriksanya dalam beberapa jam setelah saya tiba di rumah. Saya tidak ingin mengajukan PR sampai kita mengetahuinya.
EDIT: Jadi alasan saya tidak berpikir Anda perlu mengurutkan pembelian adalah karena expired_date_ms mengembalikan yang sama terlepas dari pembelian mana yang Anda tanyakan. Apakah ini tidak sama untuk kalian? Atau ada sedikit informasi yang Anda butuhkan yang memerlukan penyortiran? Beri tau aku isi pikiranmu. Saya setuju bahwa dokumen masih harus menjelaskan bahwa pembelian tidak dipesan secara kronologis, tetapi sejauh yang saya tahu penyortiran tidak diperlukan untuk mendapatkan tanggal kedaluwarsa.

@kevinEsherick Terima kasih! Dalam kasus saya, baik pembelian maupun pembaruanHistory tidak dipesan.

@kevinEsherick @andrewzey Bisakah kalian memberikan PR pada solusi ini? Kami ingin berbagi dengan orang lain yang menderita.

Tentu saya akan mempersiapkannya tepat setelah kami selesai dengan peluncuran aplikasi publik kami =).

Saya menunggu lebih banyak percakapan mengenai komentar terakhir saya. Saya mengangkat beberapa masalah yang masih belum terselesaikan. Silakan lihat di atas untuk referensi. Setelah kami menyelesaikannya, salah satu dari kami dapat membuat PR.

@kevinEsherick Ah maaf, saya melewatkan itu.

expires_date_ms jelas unik bagi saya pada setiap tanda terima perpanjangan. Mungkin saya salah paham? Saya perhatikan bahwa susunan tanda terima pembaruan yang dilampirkan pada setiap tanda terima yang didekodekan adalah sama terlepas dari tanda terima mana yang dipilih, jadi saya dapat melihat bahwa Anda mungkin benar dalam hal berikut (dari contoh saya) yang tidak diperlukan:

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

Tapi saya tetap melakukannya untuk mencoba menjadi akurat, dan mengkompensasi kemungkinan perbedaan dalam respons API yang sebenarnya dari apa yang didokumentasi Apple. Saya tidak berpikir operasi penyortiran di sana akan sangat mahal, mengingat jumlah total tanda terima yang mungkin Anda temui untuk langganan perpanjangan otomatis.

Ada ETA di PR? Saya sedang mempertimbangkan untuk pindah dari paket ini karena tidak menangani kasus ini sama sekali.

@schhumannd Mengingat bahwa PR hanya memperbarui dokumen, Anda dapat pindah ke react-native-iap sekarang.

Saya telah mengonfirmasi bahwa cuplikan kode saya benar-benar berfungsi tanpa masalah dalam produksi dengan aplikasi kami yang baru diluncurkan: https://itunes.apple.com/us/app/rp-diet/id1330041267?ls=1&mt=8

EDIT Saya pasti akan membuat PR (saya memilikinya di daftar "Berikan kembali ke OSS" di Trello kami), tetapi setelah kegilaan akhir pekan Jumat hitam

@schhumannd Saya @andrewzey . Semua yang akan dikatakan PR terkandung di sini di posting ini. Saya bermaksud untuk melakukannya, tetapi kami butuh beberapa saat untuk memilah dengan tepat apa yang perlu dilakukan, dan itu bercampur dengan perjalanan dan jam mulai yang sibuk berarti saya belum punya waktu untuk melakukan PR. Saya masih berencana untuk segera, tetapi siapa pun dapat meningkatkan sementara itu jika mereka mau. Dan @andrewzey selamat

Saya tidak yakin bahwa utas ini secara akurat mendokumentasikan cara yang benar untuk mengelola langganan yang diperpanjang secara otomatis di iOS tetapi sebaliknya menjelaskan alur kerja "solusi" per API yang diekspos oleh perpustakaan ini. Namun, ini sepenuhnya spekulasi di pihak saya dan saya sangat sadar bahwa saya bisa saja salah, jadi saya ingin umpan balik tentang pengamatan yang telah saya uraikan di bawah ini.

Saya telah meneliti cara yang benar untuk mengelola langganan perpanjangan otomatis di React Native selama berminggu-minggu tetapi tidak berhasil, jadi saya mengganti persneling dan mulai meneliti bagaimana mengelolanya secara asli di Objective-C/Swift. Ini memberi saya titik referensi untuk membandingkan API StoreKit asli dan penggunaan yang terdokumentasi dengan apa yang telah diterapkan di perpustakaan ini.

Tulisan ini adalah penjelasan terbaik yang saya temukan sejauh ini tentang bagaimana pembelian langganan, validasi, dan pemulihan harus ditangani oleh aplikasi iOS asli: https://www.raywenderlich.com/659-in-app-purchases-auto -renewable-subscriptions-tutorial dan sementara semua API digunakan di react-native-iap , mereka menerapkan alur kerja yang berbeda.

Perbedaan utama yang saya lihat adalah penggunaan appStoreReceiptUrl .

StoreKit menyediakan appStoreReceiptUrl yang, menurut pemahaman saya, adalah cara yang benar untuk memuat informasi pembelian/penerimaan saat ini untuk aplikasi iOS. Sementara saya melihat ini digunakan dan direferensikan dalam kode react-native-iap , tampaknya berada dalam konteks yang berbeda dari alur kerja yang didokumentasikan dalam artikel yang dirujuk.

Saya tidak yakin apakah/bagaimana perbedaan implementasi ini bermasalah, tetapi API react-native-iap tampaknya membuat ketergantungan yang tidak perlu pada restoreCompletedTransactions StoreKit yang akhirnya disebut oleh getPurchaseHistory .

Tampaknya bagi saya bahwa react-native-iap API harus mendukung alur kerja berikut untuk iOS:

  • Mencoba memuat tanda terima aplikasi saat ini (melalui appStoreReceiptUrl ) saat memulai aplikasi
  • Berikan kesempatan untuk memvalidasi tanda terima
  • Jika appStoreReceiptUrl adalah null, mulai "pemulihan" pada perangkat saat ini yang kemudian akan menetapkan nilai appStoreReceiptUrl dan logika validasi dapat dicoba kembali

pikiran?

Sekali lagi, saya mungkin salah memahami implementasi perpustakaan ini atau implementasi asli yang dirujuk.

@sellmeadog

Saya pikir poin kunci yang juga disebutkan dalam artikel yang Anda tautkan adalah ini:

Catatan: Aplikasi proyek menjalankan SelfieService, yang menerima tanda terima dan mengunggahnya langsung ke layanan validasi tanda terima Apple. Ini adalah "cheat" untuk menjaga agar tutorial tetap fokus pada pengaturan langganan. Dalam aplikasi nyata, Anda harus menggunakan server jarak jauh untuk ini — bukan aplikasi.

Butuh beberapa waktu bagi saya untuk memikirkan seluruh hal langganan yang dapat diperbarui secara otomatis, tetapi dari apa yang saya pahami, cara terbaik untuk menanganinya adalah menggunakan backend Anda sendiri sebagai satu-satunya sumber kebenaran:

  • kirim semua data transaksi segera ke backend Anda
  • menyediakan beberapa cara mengirim ulang jika terjadi masalah transmisi (misalnya memulihkan pembelian)
  • secara berkala (misalnya sekali sehari) verifikasi semua tanda terima yang tersimpan di backend Anda
  • backend kueri aplikasi untuk status berlangganan pada misalnya kembali dari latar belakang.

Bagi saya ini tampaknya satu-satunya cara yang masuk akal untuk menangani langganan. Saya juga telah memverifikasi proses ini dengan pengembang lain menggunakan langganan yang dapat diperbarui secara otomatis untuk beberapa waktu.

Jadi jawaban saya untuk pertanyaan OP adalah: periksa langganan secara berkala di backend, bukan di frontend

@shumannd

Saya tidak yakin kita memahami atau membicarakan masalah yang sama.

Validasi tanda terima harus dilakukan di server. Per dokumentasi Apple sendiri :

Jangan panggil titik akhir server App Store /verifyReceipt dari aplikasi Anda.

Saya menafsirkan "curang" dalam artikel yang direferensikan sebagai panggilan langsung aplikasi sampel ke /verifyReceipt langsung dari aplikasi untuk singkatnya alih-alih mendelegasikan ke server backend.

Tampaknya bagi saya bahwa Apple / App Store adalah dan harus menjadi satu-satunya sumber kebenaran. Apple pada akhirnya memproses pembelian, pembaruan, pembatalan, dll. dan membuat tanda terima yang sesuai dan membuat data tersebut tersedia melalui API StoreKit .

react-native-iap menangani pembelian dan pemulihan pembelian tanpa masalah, tetapi menurut saya masih ada celah dalam mengakses data tanda terima App Store yang tersedia secara lokal di appStoreReceiptUrl yang lagi-lagi merupakan cara terdokumentasi Apple untuk mengakses tanda terima data :

Untuk mengambil data tanda terima, gunakan metode appStoreReceiptURL dari NSBundle untuk menemukan tanda terima aplikasi...

Alur kerja yang saya pahami dari membaca dokumen Apple dan mereferensikan implementasi Object-C/Swift asli adalah sebagai berikut:

  • Muat produk untuk dibeli
  • Membeli produk (habis, tidak habis pakai, berlangganan, tidak masalah)
  • Tanda terima untuk pembelian itu disimpan secara lokal di appStoreReceiptUrl oleh StoreKit belakang layar
  • Aplikasi harus mengirim data tanda terima ke server backend (bukan App Store /verifyReceipt secara langsung) untuk validasi sesuai kebutuhan
  • appStoreReceiptUrl diperbarui oleh StoreKit setiap kali pembatalan dan/atau pembaruan terjadi (dengan asumsi delegasi StoreKit yang benar terdaftar dengan benar)
  • Ketika appStoreReceiptUrl adalah null, karena pengguna menggunakan perangkat baru, dll., gunakan alur kerja pembelian pemulihan yang akan mengambil data penerimaan saat ini dari Apple / AppStore dan StoreKit akan mempertahankan ini secara lokal di appStoreReceiptUrl balik layar

Sekali lagi, react-native-iap menangani semua ini kecuali untuk memuat data tanda terima dari appStoreReceiptUrl . Saya pikir metode getReceipt yang menanyakan tanda terima lokal akan meringankan beban mengelola langganan dan/atau pembelian lainnya.

Apple telah membuat ini jauh lebih sulit untuk dipikirkan daripada yang seharusnya.

appStoreReceiptUrl diperbarui oleh StoreKit setiap kali pembatalan dan/atau pembaruan terjadi (dengan asumsi delegasi StoreKit yang tepat terdaftar dengan benar)

Ini menjelaskan masalah yang saya miliki dengan pendekatan ini: Anda tidak mendapatkan pemberitahuan tentang pembaruan/pembatalan/kedaluwarsa dll. jika pengguna tidak membuka aplikasi Anda setelah itu dengan koneksi internet yang berfungsi.

Selain itu, sepertinya alternatif yang baik untuk pendekatan yang saya jelaskan. Saya ingin tahu mana yang paling banyak digunakan untuk aplikasi produksi.

@sellmeadog Saya pikir itu ide yang sama yang saya posting di sini: #356
Memvalidasi tanda terima secara lokal adalah salah satu metode, Apple merekomendasikan. Itu sepenuhnya independen dari permintaan server / jaringan apa pun.

Ya. Ada kerugian mengenai "tidak ada koneksi jaringan - tidak ada validasi".
Tetapi ada kasus penggunaan untuk itu.
Saya ingin validasi tanda terima yang cepat pada setiap peluncuran aplikasi dan tidak ingin melacak sendiri pembelian dalam aplikasi melalui UserDefaults atau Keychain . StoreKit sudah menyediakan mekanisme penyimpanan.

Bagaimana Anda bisa memeriksa apakah pengguna iOS memperbarui atau membatalkan langganan mereka tanpa meminta mereka untuk masuk ke akun itunes melalui sesuatu seperti getAvailablePurchases?

Misalnya:
Setelah pembelian langganan pertama, kami memvalidasi tanda terima dan menyimpan tanggal kedaluwarsa ke profil firebase mereka. Kami menggunakan node firebase untuk memeriksa saat start-up apakah mereka memiliki langganan aktif. Masalah muncul ketika tanggal kedaluwarsa telah berlalu dan periode penagihan baru dimulai. Bagaimana kami dapat memeriksa untuk melihat apakah mereka diperpanjang secara otomatis atau dibatalkan? Seperti yang disebutkan, kami menggunakan getAvailablePurchases, tetapi itu meminta login itunes seperti tombol pemulihan -- yang merupakan UI yang buruk

Saya juga bertanya-tanya hal yang sama.

Di aplikasi kami, kami tidak memerlukan ini, karena kami memiliki API sisi server yang berkomunikasi langsung dengan Apple untuk memeriksa statusnya. Setelah pembelian asli, data tanda terima disimpan di server kami, dan kemudian digunakan untuk meminta status terbaru.

Namun, saya yakin ini masih mungkin dilakukan di sisi perangkat. Saya bukan ahli dalam hal ini, tetapi menurut pemahaman saya, aplikasi akan menerima transaksi/tanda terima yang diperbarui dari OS setiap kali pembaruan terjadi. Saya menduga bahwa plugin ini menangani tanda terima ini pada peluncuran aplikasi. (Saya melihat banyak log ketika menjalankan dari Xcode, yang tampaknya memproses banyak transaksi) TAPI, dari apa yang saya tahu, tidak ada cara untuk memanfaatkan peristiwa-peristiwa itu dalam kode JS.

Bisakah kita mendapatkan info lebih lanjut tentang ini?

@csumrell @curiousdustin apakah kalian memiliki perilaku ini yang dikonfirmasi di lingkungan produksi? Saya bertanya-tanya apakah itu bisa terjadi karena kotak pasir, dengan akun kotak pasir perlu masuk karena mungkin ada akun non-kotak pasir lain di perangkat Anda, sedangkan dalam produksi pengguna mungkin akan masuk ke akun normal mereka dan ini modul mungkin bisa mendapatkan Pembelian tanpa meminta login.

Maaf, saya TIDAK mengkonfirmasi bahwa getAvailablePurchases() menyebabkan permintaan kata sandi dalam produksi.

Saya hanya berasumsi itu karena dokumen berbicara tentang metode ini yang digunakan untuk fitur yang biasa disebut sebagai Memulihkan Pembelian, yang menurut pengalaman saya melibatkan otentikasi, setidaknya di iOS.

@hyochan @JJMoon Bisakah kami mendapatkan info lebih lanjut tentang ini juga? Secara khusus, apakah ada cara untuk mendapatkan pembelian yang tersedia atau historis secara andal tanpa memicu OS untuk mencoba otentikasi? Juga silakan lihat pertanyaan saya 2 komentar di atas, mengenai kemampuan untuk bereaksi terhadap pembelian yang terdeteksi dan diproses pada peluncuran aplikasi.

@curiousdustin ya jadi ketika saya keluar dari akun iTunes normal saya dan masuk ke akun kotak pasir, itu tidak lagi meminta saya untuk masuk ketika metode ini dipanggil. Jadi tebakan/harapan saya adalah bahwa dalam produksi, di mana pengguna tidak memiliki akun kotak pasir yang digunakan, masalahnya tidak akan muncul. Pengujian beta harus bekerja dengan cara yang sama seperti produksi karena pengguna tidak memiliki akun kotak pasir yang disiapkan di perangkat, itu hanya masalah perangkat pengembangan. Jadi saya akan menguji ini besok dengan pengguna beta dan memberi tahu Anda apa yang saya temukan.
EDIT: Saya dapat mengonfirmasi bahwa ini terjadi pada perangkat non-kotak pasir. Ini adalah UX yang buruk dan cukup bermasalah. Siapapun, ada ide?

Jadi, ketika Anda memvalidasi di server Anda sendiri dengan memanggil server App Store /verifyReceipt endpoint dari aplikasi Anda, itu akan selalu mengembalikan pembaruan terkini ke info terkini untuk pengguna itu hanya dengan tanda terima pembelian pertama yang asli.

Adakah yang tahu jika menggunakan metode validasi lokal juga akan selalu mengembalikan info terbaru? Atau hanya untuk tanda terima yang Anda validasi secara lokal?

Juga, apakah ada yang punya saran untuk menyiapkan server Anda sendiri untuk memvalidasi dengan Apple? Saya tidak punya pengalaman dengan itu

@csumrell Anda bisa mendapatkan tanda terima untuk pembelian terbaru dengan menelepon getPurchaseHistory dan menyortirnya. Komentar di atas merinci bagaimana melakukan ini. Ini harus memiliki info terbaru.

Dan saya akan menjelajahi opsi untuk memvalidasi di server sehingga kami dapat saling membantu. Akan senang mendengar bagaimana orang lain melakukannya.

@kevinEsherick ya tapi getPurchaseHistory akan memicu popup login Itunes. Saya berbicara tentang menyimpan tanda terima asli ke penyimpanan lokal saat pengguna pertama kali membeli. Kemudian alih-alih memanggil getPurchaseHistory untuk memeriksa penagihan berikutnya, panggil validasiReceiptIos(originalReceipt) yang divalidasi secara lokal. Namun, saya tidak yakin apakah validasiReceiptIos mengembalikan transaksi terkini seperti yang akan dilakukan server Apple /verifyReceipt

@csumrell Gotcha. Nah itu cepat dan mudah diuji. Apakah Anda belum mencobanya? Jika tidak, saya akan memeriksanya nanti hari ini.
EDIT: @csumrell validateReceiptIos sebenarnya melacak pembaruan otomatis terbaru pada pembelian itu jadi ini adalah metode yang valid. Saya akan mengatakan bahwa ini lebih baik daripada metode yang diusulkan di atas untuk memeriksa langganan karena menghindari prompt login iTunes (kecuali seseorang dapat menemukan perbaikan untuk itu). Kekurangannya adalah Anda harus menyimpan kuitansi secara lokal. Ini seharusnya tidak terlalu merepotkan. Anda juga mungkin tidak ingin tanda terima ini masuk ke backend Anda karena alasan keamanan atau fakta bahwa jumlahnya besar (~60rb karakter), jadi itu berarti bahwa jika pengguna yang berlangganan keluar atau mulai menggunakan perangkat baru, Anda perlu meminta mereka untuk login untuk mendapatkan tanda terima pembelian terbaru mereka. Anda dapat menyimpan status langganan mereka secara internal sehingga Anda hanya meminta pengguna yang berlangganan terakhir kali Anda memeriksanya.

@hyochan telah mendanai $20,00 untuk masalah ini. Lihat di IssueHunt

@hyochan solusi spesifik apa yang Anda danai? Kami telah menyediakan beberapa di sini di utas ini, dan saya pikir komentar terbaru saya memberikan solusi terkuat dengan masalah paling sedikit. Apakah Anda mendanai seseorang untuk mengumpulkan solusi ini menjadi satu PR singkat untuk README? Atau apakah ini ada masalah kode aktual yang masih ingin Anda selesaikan?

@kevinEsherick

Apakah Anda mendanai seseorang untuk mengumpulkan solusi ini menjadi satu PR singkat untuk README?

Ya. Ini benar sekali. Juga, saya hanya ingin menguji bagaimana issue hunt dapat bekerja di luar kotak. Rencana saya selanjutnya adalah mendanai pembuatan kasus uji.

@kevinEsherick Anda benar, validasi lokal dengan validasiReceiptIos sebenarnya mengembalikan info terbaru tentang langganan hanya menggunakan ID transaksi pertama yang asli (disimpan dalam status redux)

Saya akan menggunakan metode ini untuk menghindari keharusan mengatur server saya sendiri, dan untuk menghindari popup login iTunes saat memeriksa apakah langganan aktif.

Catatan untuk mereka yang mempertimbangkan untuk menggunakan aliran validasi ini yang telah saya dan @csumrell susun : Saya telah menemukan bahwa dalam pengembangan, prompt login iTunes masih muncul pada peluncuran pertama. Ini tidak terjadi dalam produksi. Selama Anda mengikuti langkah-langkah yang telah kami uraikan, semuanya akan baik-baik saja. Mengenai mengapa melakukan ini, mungkin ketika iOS melihat bahwa IAP sedang digunakan/merupakan bagian dari peta modul dalam pengembangan, itu secara otomatis meminta masuk. Saya tidak yakin, tetapi tampaknya itu hanya fitur pengujian kotak pasir.

Hai, sepertinya tidak ada aktivitas tentang masalah ini baru-baru ini. Apakah masalah sudah diperbaiki, atau masih membutuhkan perhatian masyarakat? Masalah ini dapat ditutup jika tidak ada aktivitas lebih lanjut yang terjadi. Anda juga dapat memberi label masalah ini sebagai "Untuk Diskusi" atau "Masalah pertama yang bagus" dan saya akan membiarkannya terbuka. Terima kasih atas kontribusi Anda.

Menutup masalah ini setelah lama tidak aktif. Jika masalah ini masih ada di rilis terbaru, jangan ragu untuk membuat masalah baru dengan informasi terkini.

Mungkin seluruh aliran ini perlu diklarifikasi dalam dokumentasi?

Terutama ketika datang ke langganan perpanjangan otomatis? Sangat tidak jelas bagi pendatang baru apakah Anda dapat memeriksa expires_date_ms apakah langganannya masih aktif atau tidak?

Saya menyarankan tempat terbaik untuk ini adalah dalam contoh kerja yang lebih lengkap?

Apakah orang akan tertarik dengan ini? Jika saya punya waktu, saya bisa mengerjakan ini?

@alexpchin ya itu pasti akan menghemat banyak waktu pengembang dan karma Anda akan menjadi sangat bersih: D

Setuju! Adakah yang tertarik dengan #856 ?

Hai,

Metode @andrewzey telah bekerja dengan baik untuk kami hingga kemarin ketika kami tiba-tiba tidak lagi memvalidasi tanda terima apa pun untuk langganan kami - kami mendapatkan kesalahan penguraian JSON kembali dari iOS saat memanggil validasiReceiptIos(). Saya kira tidak ada orang lain yang mengalami ini ...? Satu-satunya perubahan yang dibuat sejak kemarin adalah penambahan kode promo di ITC yang telah kami hapus untuk membantu menghilangkan variabel. Apakah ada alasan mengapa tanda terima harus gagal dalam penguraian JSON? Gagal untuk setiap tanda terima yang dikembalikan - bukan hanya tanda terima pada indeks 0 dari sortAvailablePurchases. Kode pada dasarnya identik dengan contoh andrewzey - Saya telah menghilangkan semuanya setelah validasiReceiptIos karena kesalahan dilemparkan ke sana.

Kami menjalankan RN 0.61.4, RNIap: 4.4.2, dan iOS 13.3.1

Kami telah mencoba:

  • Menginstal ulang aplikasi
  • Akun pengguna baru
  • Menggunakan rahasia aplikasi baru
  • Menguji semua tanda terima pada setiap pengguna - tidak ada tanda terima yang tidak dapat diuraikan

Kami bertanya-tanya apakah kami tidak menggunakan finishTransactionIOS dengan benar saat melakukan pembelian kotak pasir?

Yang sangat aneh adalah ketika kami memvalidasi struk menggunakan alat online ini , semuanya terlihat normal dan kami dapat melihat semua metadata struk.

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

Pertanyaan tentang kode yang ditempatkan @andrewzey . Bukankah Date.now() menjadi waktu perangkat saat ini dan pengguna mungkin dapat mengubah ini dan memiliki langganan tanpa akhir? 🤔

Juga, bukankah memverifikasi tanda terima dari aplikasi itu sendiri tidak mengecewakan?

@doteric Bisakah Anda mengarahkan saya ke tempat mereka menyebutkan itu tidak disarankan? Saya tahu bahwa Anda dapat mengatur pemberitahuan sisi server, tetapi tampaknya jauh lebih mudah dari sudut pandang saya untuk menangani validasi ini pada klien daripada server.
Saya berjuang untuk menemukan dokumentasi yang tepat dari Apple atau sumber asli reaksi padat lainnya.

@doteric yes Date.now() adalah waktu perangkat sehingga dapat diselesaikan. Namun kemungkinan pengguna melakukan ini sangat kecil, dan bahkan metode lain dapat dikerjakan untuk berlangganan tanpa akhir. Misalnya jika menggunakan validasi sisi server sederhana, mereka dapat memasuki mode pesawat sebelum membuka aplikasi untuk mencegah aplikasi menyadari bahwa langganan telah kedaluwarsa. Tentu saja ada perlindungan lain yang dapat Anda terapkan, tetapi maksud saya adalah bahwa sisi klien yang menggunakan Date.now() tentu saja berfungsi. @captaincole Saya tidak memiliki dokumen, tetapi saya dapat mengonfirmasi bahwa saya juga telah membaca bahwa validasi sisi klien tidak disarankan. Saya membacanya di dokumen Apple, saya percaya. Yang sedang berkata, saya pikir sisi klien menyelesaikan pekerjaan.

Hai,

Saya mencoba salah satu pendekatan yang dibahas di utas ini, menggunakan validasiReceiptIos dan recent_receipt_data (membandingkan expired_date_ms dengan tanggal aktual). Ini bekerja dengan baik saat berkembang di Xcode. Namun, ketika saya mengujinya di Testflight, itu tidak berfungsi lagi. Sulit untuk di-debug jadi saya tidak dapat mengidentifikasi masalah yang sebenarnya, sepertinya tidak mendapatkan data tanda terima. Apakah ada yang memiliki masalah serupa dengan testflight?

Terima kasih sebelumnya!

Hai,

Saya mencoba salah satu pendekatan yang dibahas di utas ini, menggunakan validasiReceiptIos dan recent_receipt_data (membandingkan expired_date_ms dengan tanggal aktual). Ini bekerja dengan baik saat berkembang di Xcode. Namun, ketika saya mengujinya di Testflight, itu tidak berfungsi lagi. Sulit untuk di-debug jadi saya tidak dapat mengidentifikasi masalah yang sebenarnya, sepertinya tidak mendapatkan data tanda terima. Apakah ada yang memiliki masalah serupa dengan testflight?

Terima kasih sebelumnya!

+1

@ asobralr @ Somnus007 Saya mungkin salah, tetapi saya rasa Anda tidak dapat menguji IAP dengan testflight karena pengguna tidak dapat melakukan pembelian melalui testflight. Apple tidak memberikan peluang besar untuk menguji pembelian di lingkungan seperti produksi

@ asobralr @ Somnus007 Saya mungkin salah, tetapi saya rasa Anda tidak dapat menguji IAP dengan testflight karena pengguna tidak dapat melakukan pembelian melalui testflight. Apple tidak memberikan peluang besar untuk menguji pembelian di lingkungan seperti produksi

Benar. Anda bahkan tidak dapat mensimulasikan skenario kegagalan di kotak pasir, yang memalukan. 😞

Hai @kevinEsherick , terima kasih atas balasan Anda. Anda mungkin salah. Pengguna dapat melakukan pembelian melalui teslflight. Pada testflight, tampaknya pengguna harus masuk ke akun kotak pasir yang memiliki alamat email yang sama dengan akun apel aslinya. Kemudian semuanya bekerja dengan baik. BTW, akankah getPurchaseHistory memicu popup login Itunes di env produksi?

Sepertinya ios 14 merusak solusi ini karena ada masalah saat Anda memanggil getAvailablePurchases() sebelum memanggil requestSubscription().

Apakah halaman ini membantu?
0 / 5 - 0 peringkat