diff --git a/gui-orchid/lib/pages/purchase/purchase_page.dart b/gui-orchid/lib/pages/purchase/purchase_page.dart index 793907a0b..cd941cb7a 100644 --- a/gui-orchid/lib/pages/purchase/purchase_page.dart +++ b/gui-orchid/lib/pages/purchase/purchase_page.dart @@ -85,6 +85,7 @@ class _PurchasePageState extends State { } Widget buildPage(BuildContext context) { + // log("iap: purchase page storeDown: $_storeDown, showStoreMessage: $_showStoreMessage, storeStatus: $_storeStatus"); return SafeArea( child: Stack( children: [ @@ -119,9 +120,10 @@ class _PurchasePageState extends State { } Widget _buildStoreMessage() { + // log("iap: buildStoreMessage"); Size size = MediaQuery.of(context).size; var text = _storeStatus?.message != null - ? _storeStatus!.message + ? _storeStatus!.message! : s.theOrchidStoreIsTemporarilyUnavailablePleaseCheckBackIn; return Center( child: Container( diff --git a/gui-orchid/lib/vpn/purchase/android_purchase.dart b/gui-orchid/lib/vpn/purchase/android_purchase.dart index ba6b6e56a..19291dce9 100644 --- a/gui-orchid/lib/vpn/purchase/android_purchase.dart +++ b/gui-orchid/lib/vpn/purchase/android_purchase.dart @@ -7,7 +7,6 @@ import 'orchid_purchase.dart'; import 'package:in_app_purchase_android/billing_client_wrappers.dart'; class AndroidOrchidPurchaseAPI extends OrchidPurchaseAPI { - late BillingClient _billingClient; AndroidOrchidPurchaseAPI() : super.internal(); @@ -28,17 +27,21 @@ class AndroidOrchidPurchaseAPI extends OrchidPurchaseAPI { return OrchidPurchaseAPI.apiConfigWithOverrides(prodAPIConfig); } - @override Future initStoreListenerImpl() async { try { - _billingClient = BillingClient(_onPurchaseResult); + // UserSelectedAlternativeBillingListener alternativeBillingListener = (billingResult) { + // log('iap: alternative billing listener: $billingResult'); + // }; + UserSelectedAlternativeBillingListener? alternativeBillingListener = null; + _billingClient = + BillingClient(_onPurchaseResult, alternativeBillingListener); // _billingClient.enablePendingPurchases(); var billingResult = await _billingClient.startConnection( onBillingServiceDisconnected: () { log('iap: billing client disconnected'); }); - + if (billingResult.responseCode == BillingResponse.ok) { log('iap: billing client setup done'); } else { @@ -52,7 +55,7 @@ class AndroidOrchidPurchaseAPI extends OrchidPurchaseAPI { @override Future purchaseImpl(PAC pac) async { var billingResultWrapper = - await _billingClient.launchBillingFlow(sku: pac.productId); + await _billingClient.launchBillingFlow(product: pac.productId); log('iap: billing result response = ${billingResultWrapper.responseCode}'); } @@ -67,7 +70,8 @@ class AndroidOrchidPurchaseAPI extends OrchidPurchaseAPI { if (purchasesResult.responseCode != BillingResponse.ok) { log('iap: Error: purchase result response code: ${purchasesResult.responseCode}'); (PacTransaction.shared.get()) - ?.error('iap failed 1: responseCode = ${purchasesResult.responseCode}') + ?.error( + 'iap failed 1: responseCode = ${purchasesResult.responseCode}') .save(); return; } @@ -120,27 +124,40 @@ class AndroidOrchidPurchaseAPI extends OrchidPurchaseAPI { var skuList = OrchidPurchaseAPI.pacProductIds; log('iap: product ids requested: $skuList'); - var skuDetailsResponse = await _billingClient.querySkuDetails( - skuType: SkuType.inapp, skusList: skuList); - log('iap: sku query billing result: ${skuDetailsResponse.billingResult}'); - log('iap: sku details list: ${skuDetailsResponse.skuDetailsList}'); - - var toPAC = (SkuDetailsWrapper prod) { - double localizedPrice = prod.originalPriceAmountMicros / 1e6; - String currencyCode = prod.priceCurrencyCode; - String currencySymbol = prod.price[0]; - log('iap: originalPriceAmountMicros=${prod.originalPriceAmountMicros}, currencyCode=${prod.priceCurrencyCode}, currencySymbol=${prod.price[0]}'); - var productId = prod.sku; - return PAC( + List productList = skuList.map((sku) { + return ProductWrapper(productId: sku, productType: ProductType.inapp); + }).toList(); + var productDetailsResponse = + await _billingClient.queryProductDetails(productList: productList); + log('iap: sku query billing result: ${productDetailsResponse.billingResult}'); + log('iap: sku details list: ${productDetailsResponse.productDetailsList}'); + + PAC? Function(ProductDetailsWrapper prod) toPAC = + (ProductDetailsWrapper prod) { + final otp = prod.oneTimePurchaseOfferDetails; + if (otp == null) { + log('iap: no oneTimePurchaseOfferDetails for product: ${prod.productId}'); + return null; + } + // double localizedPrice = prod.originalPriceAmountMicros / 1e6; + double localizedPrice = otp.priceAmountMicros / 1e6; + // String currencyCode = prod.priceCurrencyCode; + String currencyCode = otp.priceCurrencyCode; + var productId = prod.productId; + var pac = PAC( productId: productId, localPrice: localizedPrice, localCurrencyCode: currencyCode, - localCurrencySymbol: currencySymbol, usdPriceExact: OrchidPurchaseAPI.usdPriceForProduct(productId), ); + log('iap: priceAmountMicros=${otp.priceAmountMicros}, currencyCode=${otp.priceCurrencyCode}, localCurrencySymbol=${pac.localCurrencySymbol}'); + return pac; }; - var pacs = skuDetailsResponse.skuDetailsList.map(toPAC).toList(); + var pacs = productDetailsResponse.productDetailsList + .map(toPAC) + .whereType() // remove nulls + .toList(); Map products = {for (var pac in pacs) pac.productId: pac}; //productsCached = products; log('iap: returning products: $products'); diff --git a/gui-orchid/lib/vpn/purchase/orchid_pac.dart b/gui-orchid/lib/vpn/purchase/orchid_pac.dart index 7e98159b1..e34c34421 100644 --- a/gui-orchid/lib/vpn/purchase/orchid_pac.dart +++ b/gui-orchid/lib/vpn/purchase/orchid_pac.dart @@ -6,7 +6,7 @@ class PAC { final String productId; final double localPrice; final String localCurrencyCode; // e.g. 'USD' - final String localCurrencySymbol; // e.g. '$' + late String localCurrencySymbol; // e.g. '$' final USD usdPriceExact; /// Format the local price as a currency value with symbol. @@ -24,9 +24,12 @@ class PAC { required this.productId, required this.localPrice, required this.localCurrencyCode, - required this.localCurrencySymbol, required this.usdPriceExact, - }); + String? localCurrencySymbol, + }) { + this.localCurrencySymbol = localCurrencySymbol ?? + NumberFormat.simpleCurrency(name: localCurrencyCode).currencySymbol; + } @override String toString() { diff --git a/gui-orchid/lib/vpn/purchase/orchid_pac_server.dart b/gui-orchid/lib/vpn/purchase/orchid_pac_server.dart index 4359b1019..d06788f43 100644 --- a/gui-orchid/lib/vpn/purchase/orchid_pac_server.dart +++ b/gui-orchid/lib/vpn/purchase/orchid_pac_server.dart @@ -377,9 +377,9 @@ class OrchidPACServer { Json.toIntSafe(responseJson['store_status'], defaultValue: 0); // parse store message - var jsonMessage = Json.trimStringOrNull(responseJson['message']); - String message = (OrchidUserConfig().getUserConfig()) - .evalStringDefault('pacs.storeMessage', jsonMessage ?? ''); + String? jsonMessage = Json.trimStringOrNull(responseJson['message']); + String? message = (OrchidUserConfig().getUserConfig()) + .evalStringDefaultNullable('pacs.storeMessage', jsonMessage); // parse the seller address map // TODO: ... @@ -462,12 +462,18 @@ class PACStoreStatus { final bool open; // Message to display or null if none. - final String message; + final String? message; // product status //Map product; PACStoreStatus({required this.message, required this.open}); + + // tostring + @override + String toString() { + return 'PACStoreStatus{open: $open, message: $message}'; + } } class PacAccount { diff --git a/gui-orchid/pubspec.yaml b/gui-orchid/pubspec.yaml index 47993ae30..e8f4347e1 100644 --- a/gui-orchid/pubspec.yaml +++ b/gui-orchid/pubspec.yaml @@ -31,19 +31,12 @@ dependencies: font_awesome_flutter: 9.0.0 http: 1.1.0 in_app_purchase_storekit: 0.3.5+2 - in_app_purchase_android: 0.2.3+7 +# in_app_purchase_android: 0.2.3+7 + in_app_purchase_android: 0.3.6+8 intl: 0.19.0 jdenticon_dart: 2.0.0 decimal: 3.0.2 - - # Tracking this issue which has been merged but is not yet in a tagged release. - # https://github.com/juliansteenbakker/mobile_scanner/issues/241 - # mobile_scanner: - # git: - # url: https://github.com/juliansteenbakker/mobile_scanner.git - # ref: 9e4e9a167980f8413c1d8860873e582d0747edd9 mobile_scanner: 5.2.1 - percent_indicator: 3.0.1 pointycastle: 3.7.3 provider: 5.0.0