convertToPurchase function
Implementation
gentype.Purchase convertToPurchase(
Map<String, dynamic> itemJson, {
required bool platformIsAndroid,
required bool platformIsIOS,
required Map<String, bool> acknowledgedAndroidPurchaseTokens,
Map<String, dynamic>? originalJson,
}) {
// Native SDKs serialize the generated Purchase types directly. Preserve the
// canonical payload as the base so newly generated optional fields are not
// silently dropped by this adapter.
final sourcePayload = normalizeDynamicMap(<String, dynamic>{
if (originalJson != null) ...originalJson,
...itemJson,
})!;
final productId = sourcePayload['productId']?.toString() ?? '';
final sourceId = sourcePayload['id']?.toString();
final sourceTransactionId = sourcePayload['transactionId']?.toString();
final dynamic quantityValue = sourcePayload['quantity'];
int quantity = 1;
if (quantityValue is num) {
quantity = quantityValue.toInt();
} else if (quantityValue is String) {
final parsedQuantity = int.tryParse(quantityValue.trim());
if (parsedQuantity != null) {
quantity = parsedQuantity;
}
}
final String? purchaseId = (sourceId?.isNotEmpty ?? false) ? sourceId : null;
if (purchaseId == null || purchaseId.isEmpty) {
debugPrint(
'[flutter_inapp_purchase] Invalid purchase payload: missing identifier',
);
throw const FormatException('Missing purchase identifier');
}
final transactionDate =
_parseTimestampMilliseconds(sourcePayload['transactionDate']) ?? 0;
if (platformIsAndroid) {
final hasSelectedPurchaseState = sourcePayload.containsKey('purchaseState');
final stateValue = _coerceAndroidPurchaseState(
sourcePayload['purchaseState'],
hasSelectedState: hasSelectedPurchaseState,
);
final purchaseState = _mapAndroidPurchaseState(stateValue).toJson();
// Determine store from input or default based on platform
final storeValue = sourcePayload['store']?.toString() ?? 'google';
final map = <String, dynamic>{
...sourcePayload,
'id': purchaseId,
'productId': productId,
'store': storeValue,
'isAutoRenewing': sourcePayload['isAutoRenewing'] as bool? ?? false,
'purchaseState': purchaseState,
'quantity': quantity,
'transactionDate': transactionDate,
'purchaseToken': sourcePayload['purchaseToken']?.toString(),
'autoRenewingAndroid': sourcePayload['autoRenewingAndroid'] as bool?,
'currentPlanId': sourcePayload['currentPlanId']?.toString(),
'dataAndroid': sourcePayload['dataAndroid']?.toString(),
'developerPayloadAndroid':
sourcePayload['developerPayloadAndroid']?.toString(),
'ids': _toStringList(sourcePayload['ids']),
'isAcknowledgedAndroid': sourcePayload['isAcknowledgedAndroid'] as bool?,
'obfuscatedAccountIdAndroid':
sourcePayload['obfuscatedAccountIdAndroid']?.toString(),
'obfuscatedProfileIdAndroid':
sourcePayload['obfuscatedProfileIdAndroid']?.toString(),
'packageNameAndroid': sourcePayload['packageNameAndroid']?.toString(),
'signatureAndroid': sourcePayload['signatureAndroid']?.toString(),
// Pending/orderless Play purchases legitimately have no order ID.
'transactionId': sourceTransactionId,
};
final purchaseToken = sourcePayload['purchaseToken']?.toString();
if (purchaseToken != null && purchaseToken.isNotEmpty) {
acknowledgedAndroidPurchaseTokens[purchaseToken] =
sourcePayload['isAcknowledgedAndroid'] as bool? ?? false;
}
return gentype.PurchaseAndroid.fromJson(map);
}
if (platformIsIOS) {
final stateIOS =
_parsePurchaseStateIOS(sourcePayload['purchaseState']).toJson();
final originalTransactionDateIOS = _parseTimestampMilliseconds(
sourcePayload['originalTransactionDateIOS'],
);
// Determine store from input or default based on platform
final storeValueIOS = sourcePayload['store']?.toString() ?? 'apple';
if (sourceTransactionId == null || sourceTransactionId.isEmpty) {
throw const FormatException('Missing iOS transactionId');
}
final map = <String, dynamic>{
...sourcePayload,
'id': purchaseId,
'productId': productId,
'store': storeValueIOS,
'isAutoRenewing': sourcePayload['isAutoRenewing'] as bool? ?? false,
'purchaseState': stateIOS,
'quantity': quantity,
'transactionDate': transactionDate,
'purchaseToken': sourcePayload['purchaseToken']?.toString(),
'ids': _toStringList(sourcePayload['ids']),
'appAccountToken': sourcePayload['appAccountToken']?.toString(),
'appBundleIdIOS': sourcePayload['appBundleIdIOS']?.toString(),
'countryCodeIOS': sourcePayload['countryCodeIOS']?.toString(),
'currencyCodeIOS': sourcePayload['currencyCodeIOS']?.toString(),
'currencySymbolIOS': sourcePayload['currencySymbolIOS']?.toString(),
'environmentIOS': sourcePayload['environmentIOS']?.toString(),
'expirationDateIOS':
_parseTimestampMilliseconds(sourcePayload['expirationDateIOS']),
'originalTransactionIdentifierIOS':
sourcePayload['originalTransactionIdentifierIOS']?.toString(),
'originalTransactionDateIOS': originalTransactionDateIOS,
'subscriptionGroupIdIOS':
sourcePayload['subscriptionGroupIdIOS']?.toString(),
'transactionId': sourceTransactionId,
'transactionReasonIOS': sourcePayload['transactionReasonIOS']?.toString(),
'webOrderLineItemIdIOS':
sourcePayload['webOrderLineItemIdIOS']?.toString(),
'revocationDateIOS':
_parseTimestampMilliseconds(sourcePayload['revocationDateIOS']),
'revocationReasonIOS': sourcePayload['revocationReasonIOS']?.toString(),
'revocationTypeIOS': sourcePayload['revocationTypeIOS']?.toString(),
'bundleOriginalTransactionIdIOS':
sourcePayload['bundleOriginalTransactionIdIOS']?.toString(),
'bundleProductIdIOS': sourcePayload['bundleProductIdIOS']?.toString(),
'bundleSubscriptionGroupIdIOS':
sourcePayload['bundleSubscriptionGroupIdIOS']?.toString(),
'bundleTransactionIdIOS':
sourcePayload['bundleTransactionIdIOS']?.toString(),
'previousOriginalTransactionIdIOS':
sourcePayload['previousOriginalTransactionIdIOS']?.toString(),
};
return gentype.PurchaseIOS.fromJson(map);
}
throw const FormatException('Unsupported purchase platform');
}