-
Notifications
You must be signed in to change notification settings - Fork 158
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Initial implementation of purchase collection. #308
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Faltam ainda algumas decisões sobre como os serviços e repositórios devem se comunicar e qual a responsabilidade de cada um, acho interessante falarmos sobre isso numa próxima reunião.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isso é realmente necessário? Se sim, deveria estar nos outros manifestos também.
Verificar principalmente o exported
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Esse é um exemplo, melhor não subir ele junto, manter para testes, ou fazer um deck real pra já inserirmos
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Acho q renomeia esse cara pra mock, pra depois nós tirarmos ele
ios/Runner.xcodeproj/project.pbxproj
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isso foi gerado por ti para fazer o revenueCat funcionar ou foi automático pelo XCode?
@@ -14,6 +14,9 @@ String descriptionForException(BaseException exception) { | |||
|
|||
case ExceptionType.failedToOpenUrl: | |||
return 'Algo deu errado ao tentar abrir o link!'; | |||
|
|||
case ExceptionType.failedPurchase: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
case ExceptionType.failedPurchase: | |
case ExceptionType.failedToPurchase: |
@@ -30,6 +30,10 @@ const collectionsSectionHeaderSeeAll = 'Ver todos'; | |||
|
|||
const collectionsStartNow = 'Começar Agora'; | |||
|
|||
const collectionPurchaseDeck = r'Comprar deck - R$ 0.99'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isso deveria ser variável com o valor.
const collectionPurchaseDeck = r'Comprar deck - R$ 0.99'; | |
String collectionPurchaseDeck(double price) => r'Comprar deck - R$ $(price.toStringAsFixed(2))'; |
Future<bool> isVisible({required String id}) async { | ||
final collection = await collectionRepo.getCollection(id: id); | ||
|
||
if (collection.isPremium) { | ||
final isAvailable = await collectionPurchaseRepo.isAvailable(); | ||
final isPurchased = await collectionPurchaseRepo.getPurchasesInfo(); | ||
|
||
if (isAvailable.contains(collection.appStoreId) || isAvailable.contains(collection.playStoreId)) { | ||
return !isPurchased.contains(collection.appStoreId) || !isPurchased.contains(collection.playStoreId); | ||
} | ||
} | ||
return collection.isPremium; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
De novo, o isPremium é uma coisa, o isVisible é outra. N deveriam estar associados.
Future<void> _purchaseInAppCollection(Collection collection) async { | ||
switch (env.platform) { | ||
case SupportedPlatform.ios: | ||
await collectionPurchaseRepo.purchaseInApp(storeId: collection.appStoreId); | ||
break; | ||
case SupportedPlatform.android: | ||
await collectionPurchaseRepo.purchaseInApp(storeId: collection.playStoreId); | ||
break; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NIP - Isso está ok, mas ficaria mais claro se tu só retornasse a string e no método tu chamasse o repositório, exemplo:
Future<void> _purchaseInAppCollection(Collection collection) async { | |
switch (env.platform) { | |
case SupportedPlatform.ios: | |
await collectionPurchaseRepo.purchaseInApp(storeId: collection.appStoreId); | |
break; | |
case SupportedPlatform.android: | |
await collectionPurchaseRepo.purchaseInApp(storeId: collection.playStoreId); | |
break; | |
} | |
} | |
// Dentro do repositório | |
await collectionPurchaseRepo.purchaseInApp(_collectionStore(collection)); | |
.... | |
String _collectionStore(Collection collection) { | |
switch (env.platform) { | |
case SupportedPlatform.ios: | |
return collection.appStoreId; | |
case SupportedPlatform.android: | |
return collection.playStoreId; | |
} | |
} |
}, | ||
); | ||
try { | ||
final isAvailbleList = await collectionPurchaseRepo.isAvailable(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
É melhor expor isso no serviço CollectionPurchaseServices, assim esse serviço nem precisa saber do collectionPurchaseRepo, já q tu quer usar esse serviço aqui. Melhor era na verdade o collection services nem saber nada sobre compra. Pra isso tu teria q angariar essas informações na vm e nela montar o card baseado no serviço de CollecionServices
e de CollectionPurchaseServices
.
@@ -15,13 +15,15 @@ void main() { | |||
description: 'This collection represents a collection.', | |||
category: 'Category', | |||
tags: const ['Tag 1', 'Tag 2'], | |||
isPremium: false, | |||
appStoreId: 'appStoreId', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
E o playStoreId?
test/fixtures/collection.json
Outdated
"Tag 2" | ||
], | ||
"isPremium": false, | ||
"appStoreId": "appStoreId", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
E o playStoreId?
@@ -35,6 +37,9 @@ class CollectionCard extends ConsumerWidget { | |||
/// List of tags associated with this collection. | |||
final List<String> tags; | |||
|
|||
/// |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing doc
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ainda falta o doc
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ainda falta o doc
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ainda falta o doc
@@ -41,6 +44,12 @@ class CollectionSerializer implements Serializer<Collection, Map<String, dynamic | |||
final rawContributors = List<Map<String, dynamic>>.from(json[CollectionKeys.contributors] as List); | |||
final contributors = rawContributors.map(contributorSerializer.from).toList(); | |||
|
|||
final isPremium = json[CollectionKeys.isPremium] as bool; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aqui teremos que ou fazer uma migração ou colocar como default false
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Acho q renomeia esse cara pra mock, pra depois nós tirarmos ele
"isPremium": true, | ||
"productInfo": { | ||
"price": 0.99, | ||
"productId": "com.olmps.memo_099_in_app_purchase_deck" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Q tal só id
?
"productInfo": { | ||
"price": null, | ||
"productId": null |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Idealmente eu nem colocaria o productInfo se n for premium
/// Updates the collection with the [id] to be premium or not. | ||
Future<void> updatePurchase({required String purchaseId}); | ||
|
||
Future<List<String>> getPurchaseProducts(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Future<List<String>> getPurchaseProducts(); | |
Future<List<String>> getPurchasedProducts(); |
/// Receives purchase information made by the user. | ||
Future<List<String>> getPurchasesInfo(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ser mais específico na documentação e no nome. Q info q é puxada? É o id do produto?
/// Updates the collection with the [id] to be premium or not. | ||
Future<void> updatePurchase({required String purchaseId}); | ||
|
||
Future<List<String>> getPurchaseProducts(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aqui tbm pode ser mais específico no nome e também falta a documentação
|
||
Future<void> _updatePurchaseCollection({required String id}) async { | ||
final collection = await collectionRepo.getCollection(id: id); | ||
final isPurchased = await purchaseRepo.getPurchasesInfo(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Esse uso do verbo no isPurchased
faz muita referencia a um booleano, aqui o ideal seria outro nome como, purchasesIds ou remotePurchases.
await Future.wait<dynamic>([ | ||
collectionPurchaseServices.updatePurchasesIfNeeded(), | ||
]); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pq não só colocar no Future.wait acima?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Colocar no readme abaixo dos sponsors
Revenue
We are currently exploring new approaches to generate revenue with the app. The implementation of the paid deck feature can be found in the code, along with an example of a paid deck. Other decks will not be visible on GitHub to ensure we can properly validate this revenue model.
await _updatePurchaseCollection(id: id); | ||
} | ||
|
||
Future<void> _updatePurchaseCollection({required String id}) async { | ||
final collection = await collectionRepo.getCollection(id: id); | ||
final isPurchased = await purchaseRepo.getPurchasesInfo(); | ||
final purchasesId = await purchaseRepo.getUserPurchases(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NIP
final purchasesId = await purchaseRepo.getUserPurchases(); | |
final purchasesIds = await purchaseRepo.getUserPurchases(); |
productInfo: ProductInfo(price: 0.0, id: ''), | ||
// productInfo: ProductInfo(price: 0.0, id: ''), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fazer o teste da serialização do productInfo
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM 🚀
Realizei a integração do RevenueCat com a aplicação.
Foram criados novos
Gateway
,Repository
eService
para a compra do deck pago.Estamos utilizando o RevenueCat como backend da nossa aplicação para verificar se o deck foi comprado ou não, ainda estamos em testes.
Caso não haja internet é mostrado somente os decks que são gratuitos ou que já são comprados.
Os decks pagos são setados no JSON como
isPremium
, caso sejamisPremium
eles são classificados como!isVisible
até que sejam comprados e tornadosisVisible
e sendo liberados juntamente com os decks restantes.Está pendente ainda a inclusão e configuração da PlayStoreID.
Ainda faltam serem inclusos os arquivos
StoreKitConfig.storekit
eStoreKitTestCertificate.cer
.