-
Notifications
You must be signed in to change notification settings - Fork 195
/
purchase_client.dart
129 lines (111 loc) · 3.97 KB
/
purchase_client.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import 'dart:async';
import 'package:clock/clock.dart';
import 'package:flutter/material.dart';
import 'package:in_app_purchase/in_app_purchase.dart';
import 'package:in_app_purchase_platform_interface/in_app_purchase_platform_interface.dart';
import 'package:purchase_client/src/products.dart';
/// Extension on [PurchaseDetails] enabling copyWith.
@visibleForTesting
extension PurchaseDetailsCopyWith on PurchaseDetails {
/// Returns a copy of the current PurchaseDetails with the given parameters.
PurchaseDetails copyWith({
String? purchaseID,
String? productID,
PurchaseVerificationData? verificationData,
String? transactionDate,
PurchaseStatus? status,
bool? pendingCompletePurchase,
}) =>
PurchaseDetails(
purchaseID: purchaseID ?? this.purchaseID,
productID: productID ?? this.productID,
verificationData: verificationData ?? this.verificationData,
transactionDate: transactionDate ?? this.transactionDate,
status: status ?? this.status,
)..pendingCompletePurchase =
pendingCompletePurchase ?? this.pendingCompletePurchase;
}
/// {@template purchase_client}
/// A PurchaseClient stubbing InAppPurchase implementation.
///
/// For real implementation, see [InAppPurchase].
/// {@endtemplate}
class PurchaseClient implements InAppPurchase {
/// {@macro purchase_client}
PurchaseClient();
/// The duration after which [isAvailable] completes with `true`.
static const _isAvailableDelay = Duration(milliseconds: 100);
@override
Stream<List<PurchaseDetails>> get purchaseStream => _purchaseStream.stream;
final StreamController<List<PurchaseDetails>> _purchaseStream =
StreamController<List<PurchaseDetails>>.broadcast();
/// This method is not implemented as the scope of this template
/// is limited to purchasing subscriptions which are non-consumables.
@override
Future<bool> buyConsumable({
required PurchaseParam purchaseParam,
bool autoConsume = true,
}) =>
throw UnimplementedError();
@override
Future<bool> buyNonConsumable({required PurchaseParam purchaseParam}) async {
final purchaseDetails = PurchaseDetails(
productID: purchaseParam.productDetails.id,
verificationData: PurchaseVerificationData(
localVerificationData: purchaseParam.applicationUserName!,
serverVerificationData: purchaseParam.applicationUserName!,
source: 'local',
),
status: PurchaseStatus.pending,
transactionDate: clock.now().millisecondsSinceEpoch.toString(),
);
_purchaseStream
..add([purchaseDetails])
..add([
purchaseDetails.copyWith(
status: PurchaseStatus.purchased,
pendingCompletePurchase: true,
),
]);
return true;
}
@override
Future<void> completePurchase(PurchaseDetails purchase) async {
_purchaseStream.add([purchase.copyWith(pendingCompletePurchase: false)]);
}
@override
T getPlatformAddition<T extends InAppPurchasePlatformAddition?>() {
throw UnimplementedError();
}
@override
Future<bool> isAvailable() async {
return Future<bool>.delayed(_isAvailableDelay, () => true);
}
@override
Future<ProductDetailsResponse> queryProductDetails(Set<String> identifiers) {
final notFoundIdentifiers = identifiers
.where((identifier) => !availableProducts.containsKey(identifier))
.toList();
return Future.value(
ProductDetailsResponse(
productDetails: identifiers
.map((identifier) => availableProducts[identifier])
.whereType<ProductDetails>()
.toList(),
notFoundIDs: notFoundIdentifiers,
),
);
}
@override
Future<void> restorePurchases({String? applicationUserName}) {
// No purchases are restored
// in this stubbed implementation of InAppPurchase.
return Future.value();
}
@override
/// This method is not implemented as the scope of this template
/// is limited.
Future<String> countryCode() {
throw UnimplementedError();
}
}