Skip to content
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

Obfuscated account id #299

Merged
merged 4 commits into from
Jul 21, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ios/Classes/FlutterInappPurchasePlugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
}
} else if ([@"buyProduct" isEqualToString:call.method]) {
NSString* identifier = (NSString*)call.arguments[@"sku"];
NSString* usernameHash = (NSString*)call.arguments[@"forUser"];
SKProduct *product;

for (SKProduct *p in validProducts) {
Expand All @@ -80,6 +81,7 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
}
if (product) {
SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:product];
payment.applicationUsername = usernameHash;
[[SKPaymentQueue defaultQueue] addPayment:payment];
} else {
NSDictionary *err = [NSDictionary dictionaryWithObjectsAndKeys:
Expand Down
28 changes: 9 additions & 19 deletions lib/flutter_inapp_purchase.dart
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ class FlutterInappPurchase {
///
/// `iOS` also returns subscriptions.
Future<List<IAPItem>> getProducts(List<String> skus) async {
if (skus == null || skus.contains(null)) return [];
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No use as List can't be null and any element of it can't be null as well

skus = skus.toList();
if (_platform.isAndroid) {
dynamic result = await _channel.invokeMethod(
Expand Down Expand Up @@ -128,7 +127,6 @@ class FlutterInappPurchase {
///
/// `iOS` also returns non-subscription products.
Future<List<IAPItem>> getSubscriptions(List<String> skus) async {
if (skus == null || skus.contains(null)) return [];
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No use as List can't be null and any element of it can't be null as well

skus = skus.toList();
if (_platform.isAndroid) {
dynamic result = await _channel.invokeMethod(
Expand Down Expand Up @@ -219,28 +217,29 @@ class FlutterInappPurchase {

/// Request a purchase on `Android` or `iOS`.
/// Result will be received in `purchaseUpdated` listener or `purchaseError` listener.
///
///
/// Check [AndroidProrationMode] for valid proration values
/// Identical to [requestSubscription] on `iOS`.
Future requestPurchase(
String sku, {
String? obfuscatedAccountIdAndroid,
String? obfuscatedProfileIdAndroid,
String? obfuscatedAccountId,
String? purchaseTokenAndroid,
String? obfuscatedProfileIdAndroid,
}) async {
if (_platform.isAndroid) {
return await _channel.invokeMethod('buyItemByType', <String, dynamic>{
'type': EnumUtil.getValueString(_TypeInApp.inapp),
'sku': sku,
'oldSku': null,
'prorationMode': -1,
'obfuscatedAccountId': obfuscatedAccountIdAndroid,
'obfuscatedAccountId': obfuscatedAccountId,
'obfuscatedProfileId': obfuscatedProfileIdAndroid,
'purchaseToken': purchaseTokenAndroid,
});
} else if (_platform.isIOS) {
return await _channel.invokeMethod('buyProduct', <String, dynamic>{
'sku': sku,
'forUser': obfuscatedAccountId,
});
}
throw PlatformException(
Expand All @@ -251,7 +250,7 @@ class FlutterInappPurchase {
/// Result will be received in `purchaseUpdated` listener or `purchaseError` listener.
///
/// **NOTICE** second parameter is required on `Android`.
///
///
/// Check [AndroidProrationMode] for valid proration values
/// Identical to [requestPurchase] on `iOS`.
Future requestSubscription(
Expand Down Expand Up @@ -506,7 +505,6 @@ class FlutterInappPurchase {
Duration duration: const Duration(days: 30),
Duration grace: const Duration(days: 3),
}) async {
assert(sku != null);
if (_platform.isIOS) {
var history =
await (getPurchaseHistory() as FutureOr<List<PurchasedItem>>);
Expand Down Expand Up @@ -548,9 +546,6 @@ class FlutterInappPurchase {
required Map<String, String> receiptBody,
bool isTest = true,
}) async {
assert(receiptBody != null);
assert(isTest != null);

final String url = isTest
? 'https://sandbox.itunes.apple.com/verifyReceipt'
: 'https://buy.itunes.apple.com/verifyReceipt';
Expand Down Expand Up @@ -587,11 +582,6 @@ class FlutterInappPurchase {
required String accessToken,
bool isSubscription = false,
}) async {
assert(packageName != null);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no use for null safety version

assert(productId != null);
assert(productToken != null);
assert(accessToken != null);

final String type = isSubscription ? 'subscriptions' : 'products';
final String url =
'https://www.googleapis.com/androidpublisher/v3/applications/$packageName/purchases/$type/$productId/tokens/$productToken?access_token=$accessToken';
Expand Down Expand Up @@ -642,7 +632,7 @@ class FlutterInappPurchase {
throw new ArgumentError('Unknown method ${call.method}');
}
return Future.value(null);
} as Future<dynamic> Function(MethodCall)?);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

redundant

});
}

Future _removePurchaseListener() async {
Expand All @@ -663,7 +653,7 @@ class FlutterInappPurchase {

/// A list of valid values for ProrationMode parameter
/// https://developer.android.com/reference/com/android/billingclient/api/BillingFlowParams.ProrationMode
class AndroidProrationMode{
class AndroidProrationMode {
/// Replacement takes effect when the old plan expires, and the new price will be charged at the same time.
/// https://developer.android.com/reference/com/android/billingclient/api/BillingFlowParams.ProrationMode#DEFERRED
static const int DEFERRED = 4;
Expand All @@ -682,4 +672,4 @@ class AndroidProrationMode{

/// https://developer.android.com/reference/com/android/billingclient/api/BillingFlowParams.ProrationMode#unknown_subscription_upgrade_downgrade_policy
static const int UNKNOWN_SUBSCRIPTION_UPGRADE_DOWNGRADE_POLICY = 0;
}
}
81 changes: 32 additions & 49 deletions lib/modules.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,28 +43,22 @@ class IAPItem {

/// Create [IAPItem] from a Map that was previously JSON formatted
IAPItem.fromJSON(Map<String, dynamic> json)
: productId = json['productId'] as String?,
price = json['price'] as String?,
currency = json['currency'] as String?,
localizedPrice = json['localizedPrice'] as String?,
title = json['title'] as String?,
description = json['description'] as String?,
: productId = json['productId'] as String?, // Android Apple not null
price = json['price'] as String?, // Android Apple not null
currency = json['currency'] as String?, // Android Apple not null
localizedPrice = json['localizedPrice'] as String?, // Android not null
title = json['title'] as String?, // Android Apple not null
description = json['description'] as String?, // Android not null, Apple can be empty (or null check)
introductoryPrice = json['introductoryPrice'] as String?,
introductoryPricePaymentModeIOS =
json['introductoryPricePaymentModeIOS'] as String?,
introductoryPriceNumberOfPeriodsIOS =
json['introductoryPriceNumberOfPeriodsIOS'] as String?,
introductoryPriceSubscriptionPeriodIOS =
json['introductoryPriceSubscriptionPeriodIOS'] as String?,
introductoryPricePaymentModeIOS = json['introductoryPricePaymentModeIOS'] as String?,
introductoryPriceNumberOfPeriodsIOS = json['introductoryPriceNumberOfPeriodsIOS'] as String?,
introductoryPriceSubscriptionPeriodIOS = json['introductoryPriceSubscriptionPeriodIOS'] as String?,
introductoryPriceNumberIOS = json['introductoryPriceNumberIOS'] as String?,
subscriptionPeriodNumberIOS =
json['subscriptionPeriodNumberIOS'] as String?,
subscriptionPeriodNumberIOS = json['subscriptionPeriodNumberIOS'] as String?,
subscriptionPeriodUnitIOS = json['subscriptionPeriodUnitIOS'] as String?,
subscriptionPeriodAndroid = json['subscriptionPeriodAndroid'] as String?,
introductoryPriceCyclesAndroid =
json['introductoryPriceCyclesAndroid'] as int?,
introductoryPricePeriodAndroid =
json['introductoryPricePeriodAndroid'] as String?,
introductoryPriceCyclesAndroid = json['introductoryPriceCyclesAndroid'] as int?,
introductoryPricePeriodAndroid = json['introductoryPricePeriodAndroid'] as String?,
freeTrialPeriodAndroid = json['freeTrialPeriodAndroid'] as String?,
signatureAndroid = json['signatureAndroid'] as String?,
iconUrl = json['iconUrl'] as String?,
Expand Down Expand Up @@ -133,8 +127,7 @@ class IAPItem {
'iconUrl: $iconUrl, '
'originalJson: $originalJson, '
'originalPrice: $originalPrice, '
'discounts: $discountsIOS, '
;
'discounts: $discountsIOS, ';
}

static List<DiscountIOS>? _extractDiscountIOS(dynamic json) {
Expand All @@ -145,11 +138,10 @@ class IAPItem {
discounts = list
.map<DiscountIOS>(
(dynamic discount) => DiscountIOS.fromJSON(discount as Map<String, dynamic>),
)
)
.toList();
}


return discounts;
}
}
Expand Down Expand Up @@ -194,8 +186,7 @@ class DiscountIOS {
'price: $price, '
'localizedPrice: $localizedPrice, '
'paymentMode: $paymentMode, '
'subscriptionPeriod: $subscriptionPeriod, '
;
'subscriptionPeriod: $subscriptionPeriod, ';
}
}

Expand Down Expand Up @@ -229,21 +220,15 @@ class PurchasedItem {
transactionReceipt = json['transactionReceipt'] as String?,
purchaseToken = json['purchaseToken'] as String?,
orderId = json['orderId'] as String?,

dataAndroid = json['dataAndroid'] as String?,
signatureAndroid = json['signatureAndroid'] as String?,
isAcknowledgedAndroid = json['isAcknowledgedAndroid'] as bool?,
autoRenewingAndroid = json['autoRenewingAndroid'] as bool?,
purchaseStateAndroid =
_decodePurchaseStateAndroid(json['purchaseStateAndroid'] as int?),
purchaseStateAndroid = _decodePurchaseStateAndroid(json['purchaseStateAndroid'] as int?),
originalJsonAndroid = json['originalJsonAndroid'] as String?,

originalTransactionDateIOS =
_extractDate(json['originalTransactionDateIOS']),
originalTransactionIdentifierIOS =
json['originalTransactionIdentifierIOS'] as String?,
transactionStateIOS =
_decodeTransactionStateIOS(json['transactionStateIOS'] as int?);
originalTransactionDateIOS = _extractDate(json['originalTransactionDateIOS']),
originalTransactionIdentifierIOS = json['originalTransactionIdentifierIOS'] as String?,
transactionStateIOS = _decodeTransactionStateIOS(json['transactionStateIOS'] as int?);

/// This returns transaction dates in ISO 8601 format.
@override
Expand All @@ -254,13 +239,15 @@ class PurchasedItem {
'transactionReceipt: $transactionReceipt, '
'purchaseToken: $purchaseToken, '
'orderId: $orderId, '

/// android specific
'dataAndroid: $dataAndroid, '
'signatureAndroid: $signatureAndroid, '
'isAcknowledgedAndroid: $isAcknowledgedAndroid, '
'autoRenewingAndroid: $autoRenewingAndroid, '
'purchaseStateAndroid: $purchaseStateAndroid, '
'originalJsonAndroid: $originalJsonAndroid, '

/// ios specific
'originalTransactionDateIOS: ${originalTransactionDateIOS?.toIso8601String()}, '
'originalTransactionIdentifierIOS: $originalTransactionIdentifierIOS, '
Expand All @@ -286,7 +273,7 @@ class PurchaseResult {
this.responseCode,
this.debugMessage,
this.code,
this.message
this.message,
});

PurchaseResult.fromJSON(Map<String, dynamic> json)
Expand All @@ -296,41 +283,37 @@ class PurchaseResult {
message = json['message'] as String?;

Map<String, dynamic> toJson() => {
"responseCode": responseCode ?? 0,
"debugMessage": debugMessage ?? '',
"code": code ?? '',
"message": message ?? '',
};
"responseCode": responseCode ?? 0,
"debugMessage": debugMessage ?? '',
"code": code ?? '',
"message": message ?? '',
};

@override
String toString() {
return 'responseCode: $responseCode, '
'debugMessage: $debugMessage, '
'code: $code, '
'message: $message'
;
'message: $message';
}
}


class ConnectionResult {
final bool? connected;

ConnectionResult({
this.connected,
});

ConnectionResult.fromJSON(Map<String, dynamic> json)
: connected = json['connected'] as bool?;
ConnectionResult.fromJSON(Map<String, dynamic> json) : connected = json['connected'] as bool?;

Map<String, dynamic> toJson() => {
"connected": connected ?? false,
};
"connected": connected ?? false,
};

@override
String toString() {
return 'connected: $connected'
;
return 'connected: $connected';
}
}

Expand Down
14 changes: 12 additions & 2 deletions test/flutter_inapp_purchase_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -910,6 +910,7 @@ void main() {
};

final String sku = "testsku";
final String forUser = "testObfuscatedUser";

setUp(() {
FlutterInappPurchase(FlutterInappPurchase.private(
Expand All @@ -927,12 +928,16 @@ void main() {
});

test('invokes correct method', () async {
await FlutterInappPurchase.instance.requestPurchase(sku);
await FlutterInappPurchase.instance.requestPurchase(
sku,
obfuscatedAccountId: forUser,
);
expect(log, <Matcher>[
isMethodCall(
'buyProduct',
arguments: <String, dynamic>{
'sku': sku,
'forUser': forUser,
},
),
]);
Expand Down Expand Up @@ -1063,6 +1068,7 @@ void main() {
group('for iOS', () {
final List<MethodCall> log = <MethodCall>[];
final String sku = "testsku";
final String forUser = "testObfuscatedUser";
final dynamic result = {
"transactionDate": "1552824902000",
"transactionId": "testTransactionId",
Expand Down Expand Up @@ -1093,12 +1099,16 @@ void main() {
});

test('invokes correct method', () async {
await FlutterInappPurchase.instance.requestPurchase(sku);
await FlutterInappPurchase.instance.requestPurchase(
sku,
obfuscatedAccountId: forUser,
);
expect(log, <Matcher>[
isMethodCall(
'buyProduct',
arguments: <String, dynamic>{
'sku': sku,
'forUser': forUser,
},
),
]);
Expand Down