
Bonjour à tous! Je m'appelle Denis, je développe Apphud , un service d'analyse des abonnements auto-renouvelables dans les applications iOS.
Dans cet article, je vais vous expliquer comment configurer, implémenter et valider des abonnements auto-renouvelables dans iOS 12 et iOS 13. En bonus, je vais vous parler de points subtils et d'embûches que tous les développeurs ne prennent pas en compte.
Configurer des abonnements sur l'App Store Connect
Si vous avez déjà un ID de bundle et une application créée, vous pouvez ignorer ces étapes. Si vous créez une application pour la premiÚre fois, procédez comme suit:
Vous devez créer un ID de bundle explicite (ID d'application) sur le portail des développeurs Apple . Une page intitulée Certificats, identifiants et profils étant ouverte, accédez à l'onglet Identifiants . En juin 2019, Apple a finalement mis à jour la disposition du portail conformément à ASC (abréviation de App Store Connect).

Nouveau design pour Apple Developer Portal en 2019

L'ID de com.apphud.subscriptionstest
explicite est généralement spécifié dans le style de domaine ( com.apphud.subscriptionstest
). Dans la section Capacités , vous remarquerez que la coche en regard des achats intégrés est déjà cochée. AprÚs avoir créé le Bundle ID ( App ID ), accédez à l'App Store Connect.
Utilisateurs de test (utilisateurs Sandbox)
Pour tester les futurs achats, vous devrez créer un utilisateur test. Pour ce faire, accédez à ASC dans l'onglet Utilisateurs et accÚs , puis à Sandbox Testers.

Formulaire de création de sandbox utilisateur
Lors de la création d'un testeur, vous pouvez spécifier toutes les données inexistantes, surtout, n'oubliez pas l'e-mail et le mot de passe!
Je vais vous expliquer comment tester les achats avec des informations d'identification de test vers la fin de l'article.
Une autre étape importante est la mise en place de contrats et de données bancaires dans la section « Accords, taxes et opérations bancaires ». Si vous n'avez pas d'accord pour les applications payantes, vous ne pourrez pas tester les abonnements auto-renouvelables!
AprÚs cela, vous pouvez créer une nouvelle application dans l'App Store Connect. Spécifiez un nom unique et sélectionnez votre ID de bundle comme ID de package.

L'ID de package est votre ID de bundle
Immédiatement aprÚs la création de l'application, accédez à l'onglet Fonctionnalités.
Si vous avez déjà créé l'application, vous pouvez continuer à lire à partir d'ici.
Le processus de création d'un abonnement auto-renouvelable comprend plusieurs étapes:
1. CrĂ©ez un identifiant d'abonnement et crĂ©ez un groupe d'abonnements . Un groupe d'abonnements est un ensemble d'abonnements avec des pĂ©riodes et des prix diffĂ©rents, mais qui ouvrent la mĂȘme fonctionnalitĂ© dans l'application. De plus, dans le groupe d'abonnements, vous ne pouvez activer la pĂ©riode d'essai gratuite qu'une seule fois et un seul des abonnements peut ĂȘtre actif. Si vous souhaitez que votre application ait deux abonnements diffĂ©rents en mĂȘme temps, vous devrez crĂ©er deux groupes d'abonnements.
2. Remplir les données d'abonnement: durée, nom d'affichage dans l'App Store (à ne pas confondre avec le seul nom) et description. Si vous ajoutez le premier abonnement au groupe, vous devrez indiquer le nom d'affichage du groupe d'abonnement. N'oubliez pas d'enregistrer vos modifications plus souvent, ASC peut se bloquer à tout moment et cesser de répondre.

Ăcran d'abonnement
3. Remplir le prix de l'abonnement. Il y a deux étapes: créer des prix et des offres spéciales. Indiquez le prix réel dans n'importe quelle devise, il est automatiquement recalculé pour tous les autres pays. Offres de lancement: vous pouvez ici offrir aux utilisateurs une période d'essai gratuite ou des remises prépayées. Des promotions sont apparues récemment sur l'App Store en 2019: elles vous permettent d'offrir des remises spéciales aux utilisateurs qui se sont désabonnés et que vous souhaitez retourner.
Génération de clé secrÚte partagée
Sur la page avec une liste de tous vos abonnements créés, vous verrez la clé partagée pour le bouton de l'application . Il s'agit d'une ligne spéciale qui est nécessaire pour valider une vérification dans une application iOS. Nous devrons valider le chÚque pour déterminer le statut de l'abonnement.
La clĂ© partagĂ©e peut ĂȘtre de deux types: une clĂ© unique pour votre application ou une seule clĂ© pour votre compte. Important: en aucun cas recrĂ©er la clĂ© si vous avez dĂ©jĂ l'application dans l'App Store, sinon les utilisateurs ne pourront pas valider le chĂšque et votre application cessera de fonctionner comme prĂ©vu.

Dans cet exemple, trois groupes d'abonnements et 3 abonnements annuels sont créés.
Copiez l'ID de tous vos abonnements et la clé partagée, cela vous sera utile plus tard dans le code.
Partie logiciel
Passons Ă la partie pratique. Que faut-il pour faire un directeur commercial complet? Au minimum, les Ă©lĂ©ments suivants devraient ĂȘtre mis en Ćuvre:
Commander
Vérifier l'état de l'abonnement
Vérifier la mise à jour
Récupération de transaction (à ne pas confondre avec la mise à jour d'un chÚque!)
L'ensemble du processus d'achat peut ĂȘtre divisĂ© en 2 Ă©tapes: rĂ©ception des produits (classe SKProduct
) et initialisation du processus d'achat (classe SKPayment
). Tout d'abord, nous devons spécifier le délégué du protocole SKPaymentTransactionObserver
.
La notification IAP_PRODUCTS_DID_LOAD_NOTIFICATION
utilisée pour mettre à jour l'interface utilisateur dans une application.
Ensuite, nous écrivons une méthode pour initialiser l'achat:
func purchaseProduct(product : SKProduct, success: @escaping SuccessBlock, failure: @escaping FailureBlock){ guard SKPaymentQueue.canMakePayments() else { return } guard SKPaymentQueue.default().transactions.last?.transactionState != .purchasing else { return } self.successBlock = success self.failureBlock = failure let payment = SKPayment(product: product) SKPaymentQueue.default().add(payment) }
Le délégué SKPaymentTransactionObserver
ressemble Ă ceci:
extension IAPManager: SKPaymentTransactionObserver { public func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) { for transaction in transactions { switch (transaction.transactionState) { case .purchased: SKPaymentQueue.default().finishTransaction(transaction) notifyIsPurchased(transaction: transaction) break case .failed: SKPaymentQueue.default().finishTransaction(transaction) print("purchase error : \(transaction.error?.localizedDescription ?? "")") self.failureBlock?(transaction.error) cleanUp() break case .restored: SKPaymentQueue.default().finishTransaction(transaction) notifyIsPurchased(transaction: transaction) break case .deferred, .purchasing: break default: break } } } private func notifyIsPurchased(transaction: SKPaymentTransaction) { refreshSubscriptionsStatus(callback: { self.successBlock?() self.cleanUp() }) { (error) in
En cas d'abonnement réussi, la méthode déléguée est appelée dans laquelle la transaction a le statut purchased
.
Mais comment déterminer la date d'expiration d'un abonnement? Pour ce faire, faites une demande distincte à Apple.
Vérifier l'état de l'abonnement
Le chÚque est validé à l'aide de la demande verifyReceipt
POST Ă ââApple, nous envoyons le chĂšque cryptĂ© sous la forme d'une chaĂźne codĂ©e en base64 comme paramĂštre, et dans la rĂ©ponse, nous recevons le mĂȘme chĂšque au format JSON. Dans le tableau, la clĂ© latest_receipt_info
répertorie toutes les transactions de chaque période de chaque abonnement, y compris les périodes d'essai. Nous pouvons uniquement analyser la réponse et obtenir la date d'expiration actuelle de chaque produit.
Lors de la WWDC 2017, ils ont ajouté la possibilité de recevoir uniquement les chÚques en cours pour chaque abonnement à l'aide de la clé exclude-old-transactions
dans la demande verifyReceipt
.
func refreshSubscriptionsStatus(callback : @escaping SuccessBlock, failure : @escaping FailureBlock){
Au début de la méthode, vous pouvez voir qu'il existe un contrÎle de l'existence d'une copie locale du contrÎle. Une vérification locale peut ne pas exister, par exemple, si l'application a été installée via iTunes. S'il n'y a pas de vérification, nous ne pouvons pas exécuter la demande verifyReceipt
. Nous devons d'abord obtenir le chÚque local actuel, puis essayer de le valider à nouveau. La mise à jour de la vérification s'effectue à l'aide de la classe SKReceiptRefreshRequest
:
private func refreshReceipt(){ let request = SKReceiptRefreshRequest(receiptProperties: nil) request.delegate = self request.start() } func requestDidFinish(_ request: SKRequest) {
La mise à jour de la vérification est implémentée dans la fonction refreshReceipt()
. Si la vérification a été mise à jour avec succÚs, la méthode déléguée requestDidFinish(_ request : SKRequest)
est appelée, qui appelle à refreshSubscriptionsStatus
méthode refreshSubscriptionsStatus
.
Comment l'analyse des informations d'achat est-elle implémentée? Nous sommes retournés un objet JSON dans lequel il y a un tableau imbriqué de transactions (par la derniÚre latest_receipt_info
). Nous expires_date
le tableau, obtenons la date d'expiration à l'aide de la clé expires_date
et l'enregistrons si cette date n'est pas encore arrivée.
private func parseReceipt(_ json : Dictionary<String, Any>) {
J'ai donné un exemple simple de la façon d'extraire la date d'expiration actuelle d'un abonnement. Il n'y a pas de gestion des erreurs et, par exemple, il n'y a pas de contrÎle pour le retour d'un achat (la date d'annulation est ajoutée).
Pour déterminer si un abonnement est actif ou non, il suffit de comparer la date actuelle avec la date des valeurs par défaut de l' utilisateur par clé de produit. S'il est absent ou inférieur à la date actuelle, l'abonnement est alors considéré comme inactif.
func expirationDateFor(_ identifier : String) -> Date?{ return UserDefaults.standard.object(forKey: identifier) as? Date } let subscriptionDate = IAPManager.shared.expirationDateFor("YOUR_PRODUCT_ID") ?? Date() let isActive = subscriptionDate > Date()
La récupération de transaction est effectuée sur une seule ligne SKPaymentQueue.default().restoreCompletedTransactions()
. Cette fonction restaure toutes les transactions terminées, appelant à nouveau la méthode déléguée func paymentQueue(**_** queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction])
.
Quelle est la différence entre la mise à jour d'un chÚque à partir de la récupération de transaction?
Les deux méthodes permettent de restaurer vos données d'achat. Mais quelles sont leurs différences? Il y a une merveilleuse table avec une vidéo wwdc :

Tableau des différences de deux façons de restaurer les achats de la WWDC
Dans la plupart des cas, il vous suffit d'utiliser SKReceiptRefreshRequest()
, car nous souhaitons uniquement recevoir un chÚque pour le calcul ultérieur de la date d'expiration.
Dans le cas des abonnements auto-renouvelables, les transactions elles-mĂȘmes ne nous intĂ©ressent pas, il suffit donc de n'utiliser que la mise Ă jour des chĂšques. Cependant, il existe des cas oĂč vous devez utiliser la mĂ©thode de rĂ©cupĂ©ration de transaction: si votre application tĂ©lĂ©charge du contenu lors de l'achat (contenu hĂ©bergĂ© par Apple) ou si vous prenez toujours en charge les versions infĂ©rieures Ă iOS 7.
Test de magasinage (test de bac Ă sable)
Auparavant, pour tester les achats, vous deviez vous connecter depuis l'App Store dans les paramĂštres de votre iPhone. C'Ă©tait trĂšs gĂȘnant (par exemple, toute la bibliothĂšque musicale Apple a Ă©tĂ© effacĂ©e). Cependant, cela n'a pas besoin d'ĂȘtre fait maintenant: le compte sandbox existe dĂ©sormais sĂ©parĂ©ment du compte principal.

Le processus d'achat est similaire par rapport aux achats réels dans l'App Store, mais il y a quelques points:
Vous devrez toujours saisir le mot de passe de connexion via la fenĂȘtre systĂšme. Les achats utilisant Touch ID / Face ID ne sont toujours pas pris en charge.
Si, lors de la saisie correcte du login et du mot de passe, le systÚme demande encore et encore le mot de passe de connexion, cliquez sur «Annuler» , minimisez l'application, puis réessayez. Cela ressemble à un non-sens, mais cela fonctionne pour beaucoup. Mais parfois, aprÚs la deuxiÚme entrée du mot de passe, le processus continue.
Vous ne pourrez pas tester le processus de désinscription.
La durée des périodes d'abonnement est bien inférieure à la réalité. Et ils ne sont pas mis à jour plus de 6 fois par jour.
Quoi de neuf dans StoreKit dans iOS 13?
De la nouvelle - uniquement la classe SKStorefront
, qui donne des informations sur le pays dans lequel l'utilisateur est enregistrĂ© dans l'App Store. Cela peut ĂȘtre utile aux dĂ©veloppeurs qui utilisent diffĂ©rents abonnements pour diffĂ©rents pays. Auparavant, tout le monde vĂ©rifiait par gĂ©olocalisation ou par rĂ©gion de l'appareil, mais cela ne donnait pas de rĂ©sultat prĂ©cis. Il est dĂ©sormais trĂšs facile de dĂ©couvrir le pays dans l'App Store: SKPaymentQueue.default().storefront?.countryCode
. Un dĂ©lĂ©guĂ© de mĂ©thode a Ă©galement Ă©tĂ© ajoutĂ© si le pays de l'App Store a changĂ© au cours du processus d'achat. Dans ce cas, vous pouvez continuer ou annuler vous-mĂȘme le processus d'achat.
PiĂšges lors de l'utilisation d'abonnements
- Vérifier un chÚque directement à partir d'un appareil n'est pas recommandé par Apple. Ils en ont parlé plusieurs fois à la WWDC (à partir de 5h50) et cela est indiqué dans la documentation . Ceci n'est pas sûr car un attaquant peut intercepter des données à l'aide d'une attaque par l'homme du milieu. La bonne façon de vérifier les chÚques est la validation locale à l'aide de votre serveur.
- Il y a un problĂšme avec la vĂ©rification de la date d'expiration. Si vous n'utilisez pas votre serveur, l'heure du systĂšme sur l'appareil peut ĂȘtre remplacĂ©e par une ancienne, puis notre code donnera le mauvais rĂ©sultat - l'abonnement sera considĂ©rĂ© comme actif. Si cela ne vous convient pas, vous pouvez utiliser n'importe quel service qui Ă©met l'heure exacte du monde.
- Tous les utilisateurs ne peuvent pas bénéficier d'un essai gratuit. L'utilisateur peut réinstaller l'application aprÚs un certain temps et l'application montrera que la version d'essai est disponible comme d'habitude. Il sera correct de mettre à jour la vérification, de la valider et de vérifier dans JSON la disponibilité de la version d'essai pour cet utilisateur. Beaucoup ne le font pas.
- Si l'utilisateur a demandé un remboursement, la date d'
cancellation_date
sera ajoutée à l'abonnement JSON, mais la date d' expires_date
restera inchangée. Par conséquent, il est important de toujours vérifier la présence du champ d' cancellation_date
, qui est préférable à expires_date
. - Cela ne vaut pas la peine de mettre Ă jour la vĂ©rification chaque fois que l'application est lancĂ©e, car, premiĂšrement, cela est inutile, et deuxiĂšmement, trĂšs probablement, l'utilisateur verra la fenĂȘtre de saisie du mot de passe Apple ID. Cela vaut la peine de mettre Ă jour le chĂšque, par exemple, lorsque l'utilisateur a lui-mĂȘme cliquĂ© sur le bouton de restauration des achats.
- Comment déterminer à quel moment il vaut la peine de valider un chÚque pour obtenir la date d'expiration actuelle d'un abonnement? Vous pouvez valider le chÚque à chaque démarrage, ou uniquement à la fin de l'abonnement. Cependant, si vous ne cochez le chÚque qu'à la fin de l'abonnement, l'utilisateur qui a émis le remboursement pourra utiliser gratuitement votre application jusqu'à la fin de la période.
Conclusion
J'espĂšre que cet article vous sera utile. J'ai essayĂ© d'ajouter non seulement le code, mais aussi d'expliquer les points subtils du dĂ©veloppement. Le code complet de la classe peut ĂȘtre tĂ©lĂ©chargĂ© ici . Ce cours sera trĂšs utile pour faire la connaissance des dĂ©veloppeurs novices et de ceux qui veulent en savoir plus sur le fonctionnement de tout. Pour les applications en direct, il est recommandĂ© d'utiliser des solutions plus sĂ©rieuses, par exemple SwiftyStoreKit .
Vous souhaitez mettre en Ćuvre des abonnements dans votre application iOS en 10 minutes? IntĂ©grez Apphud et:
- Faites des achats en utilisant une seule méthode;
- suivre automatiquement l'état de l'abonnement de chaque utilisateur;
- Intégrez facilement les offres d'abonnement
- envoyer des événements d'abonnement à Amplitude, Mixpanel, Slack et Telegram en tenant compte de la devise locale de l'utilisateur;
- diminuer le taux de désabonnement dans les applications et renvoyer les utilisateurs non abonnés.