[How to] avoir des items payants dans son application

Tous les tutoriaux unity, de la communauté ou d'ailleurs.
Répondre
Avatar de l’utilisateur
Iwa
Messages : 1131
Inscription : 25 Avr 2012 16:20
Contact :

[How to] avoir des items payants dans son application

Message par Iwa » 06 Nov 2018 16:15

Hello les loulous,

ça fait un bail que je n'ai pas posté de petits tutos ici et voilà que la lutte quotidienne m'a fait traverser l'utilisation du module IAP(In-App Purchase).
Kézako? C'est tout simplement le service Unity qui permet d'avoir des éléments payants (pour de vrai) dans votre application.

Ca peut servir à trois choses différentes :
- les éléments Consummable, soit des éléments qu'on peut acheter autant de fois que l'on souhaite (ex : des balles pour une arme)
- les éléments Non Consummable, soit des éléments qu'on achètera qu'une seule fois (ex : un super gun de ouf)
- les éléments Subscription, soit un abonnement renouvelable tous les X temps.

En ce qui me concerne je devais gérer un élément non consommable pour que mon application est un statut freemium et premium.
Juste que là tout va bien et il y a de la doc sur Unity pour vous aider, mais j'avoue que j'ai été un peu pommée au départ. Parce qu'on manipule du vrai argent de la vraie vie, on a pas envie d'avoir un truc qui est complètement foireux et qui marche pas.

Il existe des outils pour faire de l'achat in App sans coder la moindre chose. J'ai essayé et j'ai pas aimé. On est plus limité, on peut pas par exemple montrer des popup dans l'appli ou autre bref, c'est sûrement bien quand on code pas mais en tant que dev, ça me gratait plus l'arrière fond du crâne qu'autre chose.

Les indispensables avant de commencer :
https://unity3d.com/fr/learn/tutorials/ ... -your-game => le tuto de base pour un peu piger de quoi ça s'agit ainsi que le package de début : mettre les services IAP et Analytics en route notamment et le fonctionnement de base.

C'est bien MAIS s'tun peu l'bazar! Vous vous prenez un code dans la tronche avec des fonctions dans tous les sens et zetes pommés de la vie. Du coup voici un petit décortiquage du code proposé, qui est une très bonne base hein ;)

Code : Tout sélectionner

using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Purchasing;

//faites ce que vous voulez, si vous n'avez pas de namescpae bazarder la ligne et les accolades correspondantes.
namespace CompleteProject
{
	//voici LA classe qui va permettre de gérer nos petits achats pépère
    public class Purchaser : MonoBehaviour, IStoreListener
    {
        private static IStoreController m_StoreController;          // The Unity Purchasing system.
        private static IExtensionProvider m_StoreExtensionProvider; // The store-specific Purchasing subsystems.
        
        //en anglais on a tout une explication longue et relou
        //et d'où c'est static ça HEIN!!! Hérétique!
        //toussa pour dire que en gros il faut faire une diff entre les ID de ses produits (sans dec?)
        //et et que eux s'amuse à rajouter quel type de produit on ajoute.
        //en gros on explique c'est bien de faire en sorte que son produit soit identifier suivant le store
        //en ce qui me concerne je n'ai pour l'instant testé qu'avec Android et en suivant un autre tuto j'ai l'impression qu'on s'en cogne
        //c'est tout bêtement par particité pour suivre l'analyse des données je pense.
        public static string kProductIDConsumable =    "consumable";   
        public static string kProductIDNonConsumable = "nonconsumable";
        public static string kProductIDSubscription =  "subscription"; 
         
        private static string kProductNameAppleSubscription =  "com.unity3d.subscription.new";
        private static string kProductNameGooglePlaySubscription =  "com.unity3d.subscription.original"; 
        
        void Start()
        {
	    //pour faire de l'achat de produit il faut en gros créer une sorte de store virtuel qui contiendra ce que le vrai store contient
	    //On initialise donc cette "copie" de store.
            if (m_StoreController == null)
            {
                InitializePurchasing();
            }
        }
        
        public void InitializePurchasing() 
        {
            if (IsInitialized())
            {
                return;
            }
            
            //On créé notre store virtuel et on va le remplir en prenant soin d'un truc giga important, le nom de l'identifiant
            var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());
            
            //On ajout un produit consommable, non consommable et un abonnement
            //l'id de chaque produit est UNIQUE et doit correspondre à ce que vous ajouterai dans le store
            builder.AddProduct(kProductIDConsumable, ProductType.Consumable);
            builder.AddProduct(kProductIDNonConsumable, ProductType.NonConsumable);
            //une autre façon d'écrire le AddProduct. Ici on spécifie des noms différents pour les stores
            //au dessus on le fait pas et j'avoue avoir opter pour la solution du dessus. 
            builder.AddProduct(kProductIDSubscription, ProductType.Subscription, new IDs(){
                { kProductNameAppleSubscription, AppleAppStore.Name },
                { kProductNameGooglePlaySubscription, GooglePlay.Name },
            });
            
            //une fois le store rempli on va faire définitivement créer notre store virtuel, copie du réel
            UnityPurchasing.Initialize(this, builder);
        }
                
        //la fonction qu'on appelle tout le temps avant de faire quoi que ce soit comme achat ou autre
        private bool IsInitialized()
        {
            return m_StoreController != null && m_StoreExtensionProvider != null;
        }
        
        //achat d'un élément consomable
        //en gros c'est la fonction que vous appelerez en cliquand sur votre joli bouton d'UI. 
        //que ce soit du consommable, non-consommable ou un abo on s'en fiche en vrai
        //l'important c'est BuyProductID(l'id unique de votre produit)
        public void BuyConsumable()
        {
            BuyProductID(kProductIDConsumable);
        }       
        public void BuyNonConsumable()
        {
            BuyProductID(kProductIDNonConsumable);
        }
        
        //petite subtilité ici : on donne l'id générique, pas celui specifique attaché Google ou Apple ;)
        public void BuySubscription()
        {
            BuyProductID(kProductIDSubscription);
        }
        
        
        void BuyProductID(string productId)
        {
            if (IsInitialized())
            {
                
                Product product = m_StoreController.products.WithID(productId);
                
                // cherchons le produit correspondant à l'identifiant et achetons le
                if (product != null && product.availableToPurchase)
                {
                    Debug.Log(string.Format("Purchasing product asychronously: '{0}'", product.definition.id));
                    //on lance l'achat qui va ensuite répondre Oui j'ai réussi ou Non j'ai pas réussi.
                    m_StoreController.InitiatePurchase(product);
                }
                // Si on c'est planté on se prend un débug dans la tête. On pourrait mettre une belle popup avec
                // une insulte en rouge pour dire au dev que c'est un gros c*n
                else
                {
                    Debug.Log("BuyProductID: FAIL. Not purchasing product, either is not found or is not available for purchase");
                }
            }
            //Si le store n'est pas initialisé, on se mange encore une info.
            else
            {
                Debug.Log("BuyProductID FAIL. Not initialized.");
            }
        }
        
        
	// Si vous êtes masochiste et que votre application doit se trouver sur Apple
	//cette petite fonction est nécessaire. Alors que les autres stores vont cherché tout seul comme des grands à regarder
	// si oui ou zut vous avez acheté un abonnement ou un non consommable (ben oui pour le consommable on s'en fiche un peu
	// les conséquences d'achat d'un consommable sont visibles dans le jeu, vos paramètres toussa, donc c'est sauvé autrement)
	// Apple ne sait pas faire. Ca signifie que si vous désinstaller l'application, Apple ne va pas savoir ce que vous avez acheter si vous
	//n'en faites pas la demande.... Il faut donc appeler cette fonction pour récupérer son dû!
	public void RestorePurchases()
        {
            if (!IsInitialized())
            {
                Debug.Log("RestorePurchases FAIL. Not initialized.");
                return;
            }
            
	   // On vérifie la plateforme parce que sur autre chose que iOS on s'en fiche
            if (Application.platform == RuntimePlatform.IPhonePlayer || 
                Application.platform == RuntimePlatform.OSXPlayer)
            {
                Debug.Log("RestorePurchases started ...");
                
                //on récup nos achats
                var apple = m_StoreExtensionProvider.GetExtension<IAppleExtensions>();
                apple.RestoreTransactions((result) => {
                    // On va recevoir les infos sur ce qu'il faut récupérer ou non. La fonction ProcessPurchase sera appelé si on a acheté des trucs. Je n'ai cependant pas encore testé les effets de cette fonction de récup.
                    Debug.Log("RestorePurchases continuing: " + result + ". If no further messages, no purchases available to restore.");
                });
            }
            else
            {
                Debug.Log("RestorePurchases FAIL. Not supported on this platform. Current = " + Application.platform);
            }
        }
        
        
        //  
        // --- IStoreListener
        // Ci dessous la mécanique d'achat réel avec l'achat réussi/rejeté et les messages qui vont avec.
        
        public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
        {
            // Purchasing has succeeded initializing. Collect our Purchasing references.
            Debug.Log("OnInitialized: PASS");
            
            // Overall Purchasing system, configured with products for this application.
            m_StoreController = controller;
            // Store specific subsystem, for accessing device-specific store features.
            m_StoreExtensionProvider = extensions;
        }
        
        
        public void OnInitializeFailed(InitializationFailureReason error)
        {
            // Purchasing set-up has not succeeded. Check error for reason. Consider sharing this reason with the user.
            Debug.Log("OnInitializeFailed InitializationFailureReason:" + error);
        }
        
        
        public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args) 
        {
            //en fonction de ce que l'on vient d'acheter on fait les différentes actions nécessaires sur l'UI ou autre.
            //si on est ici, c'est que l'achat à réussi.
            if (String.Equals(args.purchasedProduct.definition.id, kProductIDConsumable, StringComparison.Ordinal))
            {
                Debug.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id));
                //Ajouter des sous
                ScoreManager.score += 100;
            }
            else if (String.Equals(args.purchasedProduct.definition.id, kProductIDNonConsumable, StringComparison.Ordinal))
            {
                Debug.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id));
		//ajouter une super arme de fifou malade à sa collection
	    }
            else if (String.Equals(args.purchasedProduct.definition.id, kProductIDSubscription, StringComparison.Ordinal))
            {
                Debug.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id));
                //avoir un mois d'abonnement à Kikou magazine
            }
            // Si ce n'est ni consommage, ni non-consommable, ni un abo, ce produit ne devrait même pas exister et on se fait jeter :)
            else 
            {
                Debug.Log(string.Format("ProcessPurchase: FAIL. Unrecognized product: '{0}'", args.purchasedProduct.definition.id));
            }

		//On renvoit ensuite une info importante ici qui dit si on a bien traité notre achat ou si on doit s'en souvenir au prochain 
		//lancement. Si jamais on arrive pas à sauver dans le Cloud l'info qu'on a acheté le produit, on peut renvoyer
		Use PurchaseProcessingResult.Pending et il faudra le traiter au prochain lancement. Je n'ai aps encore eu l'occas de tester 
		//mais pour quelqu'un qui n'est pas connecté à internet ça doit servir :) 
            return PurchaseProcessingResult.Complete;
        }
        
        
        public void OnPurchaseFailed(Product product, PurchaseFailureReason failureReason)
        {
            Debug.Log(string.Format("OnPurchaseFailed: FAIL. Product: '{0}', PurchaseFailureReason: {1}", product.definition.storeSpecificId, failureReason));
        }
    }
}
Voilà du coup un bon squelette pour commencer. Reste à créer une UI pour acheter nos trucs, leur trouver des Id cools générique + spécifique (pas obligatoire), faire un bouton restore que par exemple pour que les gens fasse la demande de restoration de leurs items si ils ont iOS (bien fait pour vous!) et voilà.

Cool et maintenant??

Maintenant il faut configurer votre store pour tester.
Google : https://docs.unity3d.com/Manual/UnityIA ... ation.html
Apple : https://docs.unity3d.com/Manual/UnityIA ... ation.html

:!: ATTENTION : Pour gérer des achats in App pour Google il faut vous créer un compte Google merchant dans votre compte dev. C'est assez facile et peu contraignant quand aux infos demandées
:!: ATTENTION BIS : Si vous voulez faire des achats in App pour Apple, désolée les gars, mais vous allez en chier à devoir remplir un foutu formulaire de taxe américaine en anglais. Vous imaginez la paperasse administrative française et ben c'est pire et c'est en anglais!!!! Prévoyez le coup à l'avance, c'est une vraie misère et ça fait perdre les cheveux et la patience. N'hésitez pas à vous faire aider par les gentils gens du support Apple.

Une fois ceci fait, vous allez pouvoir tester sur Google par exemple (le plus rapide) vos achats. Suivez bien la doc proposée en lien, ça donne TOUT. Petit hic, l'interface Google à changer depuis mais on s'en sort.

Infos importantes tout de même qui m'ont vallu une perte de temps énorme :
Vous avez publié votre application et à présent vous ajouter de l'IAP, vous souhaitez donc tester votre script. Que faire? Faire une version de test Interne et créé un liste de testeurs.
:!: Si vous aviez déjà une alpha avant ou autre, vos testeurs doivent être dans un seul type de version (bêta, alpha, interne), sinon ça va faire un conflit qui vous empêchera de tester quoi que ce soit.
:!: Votre numéro de version de l'appli de tests DOIT être suppérieure à celle publiée pour l'outrepasser, sinon le testeur ne verra que la version publiée. Ca parait évidement puisqu'on ne peut pas mettre sur google une version égale ou inférieure. OUI MAIS dans vos player settings vous pouvez juste incrémenter le bundle version code et mettre à jour sur google. SAUF QUE c'est bien votre numéro de version qui doit surpasser la version de votre application publiée. Donc méfiance avec vos manière de remplir vos versions et bundle version code.

EDIT :
Et sur iOS ça se passe comment?
Alors j'ai pu porter mon code sur iOS et ça se passe plutôt très bien en fait ( :| :| ). La petite différence résite dans l'affaire du fameux restore. Pour l'instant j'ai opté pour une solution (on clique sur un bouton pour restore quand on a désinstaller l'appli ou qu'on se connecte d'ailleurs). Si on a jamais rien acheté ça met une popup pour dire qu'il n'y a rien associer au compte. Et quand on achète ça s'ajoute à des players prefs (beurk la sécurité :/). Je pense qu'il faudrait creuser pour forcer l'appel à la fonction Restore systématique, parce que sinon, si je faisais pas mon horreur avec les players pref ben à chaque lancement d'application PUNI! Rien, que dalle! Il dit que tu n'a rien et si tu essayes d'acheter là il te dit, hey mais en fait tu l'as déjà acheté (je n'ai que des non consommables).
Quand on a un serveur avec des données associés à un compte ça va, on peut stocker plein de choses, dont ce qu'on a acheté, du coup pas de problème de Restore en ce cas. Mais dans le cas contraire c'est pénible.
Après pour tester il y a la procédure dans les liens suivants (un testeur Sandbox et le tour est joué, c'est même pour une fois plus rapide et plus simple que Google :o ).

Du coup j'ai mon bouzin qui fonctionne en tests de partout et c'est cool. Reste à voir si je peux pas forcer le Restore à chaque fois que l'initialisation du store soit faite.

Bref encore un peu d'effort mais le gros de l'explication est entre vos mains. Au plaisir si ça peut servir.

Les liens pour pas crever et qui m'ont personnellement sauver la mise :)
https://answers.unity.com/questions/151 ... se-no.html
https://docs.unity3d.com/Manual/UnityIA ... ation.html
https://docs.unity3d.com/Manual/UnityIA ... ation.html
"N'est stupide que la stupidité Monsieur..." - Forest Gump
... sauf si tu lis pas ça :)

Si tu as tout ce qu'il te faut, merci de penser à basculer ton sujet en [RESOLU] en éditant ton tout premier post ;)

Avatar de l’utilisateur
E3DStef
Administrateur
Administrateur
Messages : 1646
Inscription : 14 Juil 2013 18:30
Localisation : https://www.carte-des-membres.com/fr/Unity3D-France/

Re: How to avoir des items payants dans son application

Message par E3DStef » 06 Nov 2018 23:05

Sympa l'info, merci Iwa :super:
Le Savoir n'est Précieux que s'il est Partagé

Si besoin urgent de me contacter, faites moi un mail sur : franceunity3d@gmail.com

Avatar de l’utilisateur
Alesk
Messages : 2303
Inscription : 13 Mars 2012 09:09
Localisation : Bordeaux - France
Contact :

Re: How to avoir des items payants dans son application

Message par Alesk » 07 Nov 2018 20:57

Merci !!

Avatar de l’utilisateur
DevAmat
Messages : 435
Inscription : 23 Nov 2016 11:50

Re: How to avoir des items payants dans son application

Message par DevAmat » 13 Nov 2018 15:40

Merci pour le partage!

Avatar de l’utilisateur
Alesk
Messages : 2303
Inscription : 13 Mars 2012 09:09
Localisation : Bordeaux - France
Contact :

Re: How to avoir des items payants dans son application

Message par Alesk » 13 Nov 2018 18:33

Question : si je souhaite lier des données supplémentaires à télécharger lors de l'achat, ça se passe comment ?

Avatar de l’utilisateur
Iwa
Messages : 1131
Inscription : 25 Avr 2012 16:20
Contact :

Re: How to avoir des items payants dans son application

Message par Iwa » 13 Nov 2018 19:03

Euh qu'est-ce que tu appeles des données supplémentaires?
"N'est stupide que la stupidité Monsieur..." - Forest Gump
... sauf si tu lis pas ça :)

Si tu as tout ce qu'il te faut, merci de penser à basculer ton sujet en [RESOLU] en éditant ton tout premier post ;)

Avatar de l’utilisateur
Alesk
Messages : 2303
Inscription : 13 Mars 2012 09:09
Localisation : Bordeaux - France
Contact :

Re: How to avoir des items payants dans son application

Message par Alesk » 14 Nov 2018 11:23

Je parles d'assets supplémentaires (textures, sons, meshes, etc...) dans un AssetBundle par exemple

Avatar de l’utilisateur
Iwa
Messages : 1131
Inscription : 25 Avr 2012 16:20
Contact :

Re: How to avoir des items payants dans son application

Message par Iwa » 15 Nov 2018 11:05

Il suffit d'avoir une base qui relie ton id de paiement à ton fameux asset. En gros tu te fais une classe TrucAAcheter avec un string pour l'id de l'élément payant, l'adresse de asset bundle lié et autres. Ensuite tu cliques sur acheter en filant en lien l'instance de cette classe TrucAAcheter qui te concerne et tu peux ensuite télécharger le bundle :).

Je le fais actuellement et ça se passe bien. Par contre je viens de tilter que Google tu as un délai de rétractation, faudra que je gère ça :).
"N'est stupide que la stupidité Monsieur..." - Forest Gump
... sauf si tu lis pas ça :)

Si tu as tout ce qu'il te faut, merci de penser à basculer ton sujet en [RESOLU] en éditant ton tout premier post ;)

Avatar de l’utilisateur
Alesk
Messages : 2303
Inscription : 13 Mars 2012 09:09
Localisation : Bordeaux - France
Contact :

Re: How to avoir des items payants dans son application

Message par Alesk » 15 Nov 2018 11:54

Ok merci !
Il se peut que je te sollicite à l'avenir pour reparler de tout ça, si tu veux bien ;)

Avatar de l’utilisateur
Iwa
Messages : 1131
Inscription : 25 Avr 2012 16:20
Contact :

Re: How to avoir des items payants dans son application

Message par Iwa » 15 Nov 2018 17:10

Pas de foufis, par contre si tu peux passer plus par le discord que ici où je passe moins souvent ;).
"N'est stupide que la stupidité Monsieur..." - Forest Gump
... sauf si tu lis pas ça :)

Si tu as tout ce qu'il te faut, merci de penser à basculer ton sujet en [RESOLU] en éditant ton tout premier post ;)

Répondre

Revenir vers « Tutoriaux »