capacitor-native-purchases 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CapacitorPurchases.podspec +17 -0
- package/Package.swift +28 -0
- package/README.md +295 -0
- package/android/build.gradle +58 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/jok/purchases/capacitor/Purchases.java +400 -0
- package/android/src/main/java/jok/purchases/capacitor/PurchasesPlugin.java +169 -0
- package/android/src/main/res/.gitkeep +0 -0
- package/dist/docs.json +701 -0
- package/dist/esm/definitions.d.ts +88 -0
- package/dist/esm/definitions.js +2 -0
- package/dist/esm/definitions.js.map +1 -0
- package/dist/esm/index.d.ts +4 -0
- package/dist/esm/index.js +7 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/web.d.ts +26 -0
- package/dist/esm/web.js +43 -0
- package/dist/esm/web.js.map +1 -0
- package/dist/plugin.cjs.js +57 -0
- package/dist/plugin.cjs.js.map +1 -0
- package/dist/plugin.js +60 -0
- package/dist/plugin.js.map +1 -0
- package/ios/Sources/PurchasesPlugin/Purchases.swift +341 -0
- package/ios/Sources/PurchasesPlugin/PurchasesPlugin.swift +116 -0
- package/ios/Tests/PurchasesPluginTests/PurchasesTests.swift +15 -0
- package/package.json +83 -0
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
package jok.purchases.capacitor;
|
|
2
|
+
|
|
3
|
+
import android.app.Activity;
|
|
4
|
+
import android.util.Log;
|
|
5
|
+
|
|
6
|
+
import com.android.billingclient.api.BillingClient;
|
|
7
|
+
|
|
8
|
+
import com.android.billingclient.api.BillingClientStateListener;
|
|
9
|
+
import com.android.billingclient.api.BillingFlowParams;
|
|
10
|
+
import com.android.billingclient.api.BillingResult;
|
|
11
|
+
import com.android.billingclient.api.ProductDetails;
|
|
12
|
+
import com.android.billingclient.api.Purchase;
|
|
13
|
+
import com.android.billingclient.api.PurchaseHistoryRecord;
|
|
14
|
+
import com.android.billingclient.api.QueryProductDetailsParams;
|
|
15
|
+
import com.android.billingclient.api.QueryPurchaseHistoryParams;
|
|
16
|
+
import com.android.billingclient.api.QueryPurchasesParams;
|
|
17
|
+
import com.getcapacitor.JSObject;
|
|
18
|
+
import com.getcapacitor.Logger;
|
|
19
|
+
import com.getcapacitor.PluginCall;
|
|
20
|
+
|
|
21
|
+
import android.content.Context;
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
import androidx.annotation.NonNull;
|
|
25
|
+
|
|
26
|
+
import java.io.BufferedReader;
|
|
27
|
+
import java.io.InputStreamReader;
|
|
28
|
+
import java.net.HttpURLConnection;
|
|
29
|
+
import java.net.URL;
|
|
30
|
+
import java.nio.charset.StandardCharsets;
|
|
31
|
+
import java.text.SimpleDateFormat;
|
|
32
|
+
import java.util.ArrayList;
|
|
33
|
+
import java.util.Calendar;
|
|
34
|
+
import java.util.List;
|
|
35
|
+
import java.util.Locale;
|
|
36
|
+
import java.util.Objects;
|
|
37
|
+
|
|
38
|
+
public class Purchases {
|
|
39
|
+
|
|
40
|
+
private final Activity activity;
|
|
41
|
+
public Context context;
|
|
42
|
+
private final BillingClient billingClient;
|
|
43
|
+
private int billingClientIsConnected = 0;
|
|
44
|
+
|
|
45
|
+
private String googleVerifyEndpoint = "";
|
|
46
|
+
private String googleBid = "";
|
|
47
|
+
|
|
48
|
+
public Purchases(SubscriptionsPlugin plugin, BillingClient billingClient) {
|
|
49
|
+
|
|
50
|
+
this.billingClient = billingClient;
|
|
51
|
+
this.billingClient.startConnection(new BillingClientStateListener() {
|
|
52
|
+
@Override
|
|
53
|
+
public void onBillingSetupFinished(@NonNull BillingResult billingResult) {
|
|
54
|
+
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
|
|
55
|
+
billingClientIsConnected = 1;
|
|
56
|
+
} else {
|
|
57
|
+
billingClientIsConnected = billingResult.getResponseCode();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
@Override
|
|
62
|
+
public void onBillingServiceDisconnected() {
|
|
63
|
+
// Try to restart the connection on the next request to
|
|
64
|
+
// Google Play by calling the startConnection() method.
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
this.activity = plugin.getActivity();
|
|
68
|
+
this.context = plugin.getContext();
|
|
69
|
+
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
public String echo(String value) {
|
|
73
|
+
Log.i("Echo", value);
|
|
74
|
+
return value;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
public void setGoogleVerificationDetails(String googleVerifyEndpoint, String bid) {
|
|
78
|
+
this.googleVerifyEndpoint = googleVerifyEndpoint;
|
|
79
|
+
this.googleBid = bid;
|
|
80
|
+
|
|
81
|
+
Log.i("SET-VERIFY", "Verification values updated");
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
public void getProductDetails(String productIdentifier, PluginCall call) {
|
|
85
|
+
|
|
86
|
+
JSObject response = new JSObject();
|
|
87
|
+
|
|
88
|
+
if (billingClientIsConnected == 1) {
|
|
89
|
+
|
|
90
|
+
QueryProductDetailsParams.Product productToFind = QueryProductDetailsParams.Product.newBuilder()
|
|
91
|
+
.setProductId(productIdentifier)
|
|
92
|
+
.setProductType(BillingClient.ProductType.SUBS)
|
|
93
|
+
.build();
|
|
94
|
+
|
|
95
|
+
QueryProductDetailsParams queryProductDetailsParams =
|
|
96
|
+
QueryProductDetailsParams.newBuilder()
|
|
97
|
+
.setProductList(List.of(productToFind))
|
|
98
|
+
.build();
|
|
99
|
+
|
|
100
|
+
billingClient.queryProductDetailsAsync(
|
|
101
|
+
queryProductDetailsParams,
|
|
102
|
+
(billingResult, productDetailsList) -> {
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
|
|
106
|
+
ProductDetails productDetails = productDetailsList.get(0);
|
|
107
|
+
String productId = productDetails.getProductId();
|
|
108
|
+
String title = productDetails.getTitle();
|
|
109
|
+
String desc = productDetails.getDescription();
|
|
110
|
+
Log.i("productIdentifier", productId);
|
|
111
|
+
Log.i("displayName", title);
|
|
112
|
+
Log.i("desc", desc);
|
|
113
|
+
|
|
114
|
+
List<ProductDetails.SubscriptionOfferDetails> subscriptionOfferDetails = productDetails.getSubscriptionOfferDetails();
|
|
115
|
+
|
|
116
|
+
String price = Objects.requireNonNull(subscriptionOfferDetails).get(0).getPricingPhases().getPricingPhaseList().get(0).getFormattedPrice();
|
|
117
|
+
|
|
118
|
+
JSObject data = new JSObject();
|
|
119
|
+
data.put("productIdentifier", productId);
|
|
120
|
+
data.put("displayName", title);
|
|
121
|
+
data.put("description", desc);
|
|
122
|
+
data.put("price", price);
|
|
123
|
+
|
|
124
|
+
response.put("responseCode", 0);
|
|
125
|
+
response.put("responseMessage", "Successfully found the product details for given productIdentifier");
|
|
126
|
+
response.put("data", data);
|
|
127
|
+
|
|
128
|
+
} catch (Exception e) {
|
|
129
|
+
Log.e("Err", e.toString());
|
|
130
|
+
response.put("responseCode", 1);
|
|
131
|
+
response.put("responseMessage", "Could not find a product matching the given productIdentifier");
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
call.resolve(response);
|
|
135
|
+
}
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
} else if (billingClientIsConnected == 2) {
|
|
139
|
+
|
|
140
|
+
response.put("responseCode", 500);
|
|
141
|
+
response.put("responseMessage", "Android: BillingClient failed to initialise");
|
|
142
|
+
call.resolve(response);
|
|
143
|
+
|
|
144
|
+
} else {
|
|
145
|
+
|
|
146
|
+
response.put("responseCode", billingClientIsConnected);
|
|
147
|
+
response.put("responseMessage", "Android: BillingClient failed to initialise");
|
|
148
|
+
|
|
149
|
+
response.put("responseCode", 503);
|
|
150
|
+
response.put("responseMessage", "Android: BillingClient is still initialising");
|
|
151
|
+
call.resolve(response);
|
|
152
|
+
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
public void getLatestTransaction(String productIdentifier, PluginCall call) {
|
|
157
|
+
|
|
158
|
+
JSObject response = new JSObject();
|
|
159
|
+
|
|
160
|
+
if (billingClientIsConnected == 1) {
|
|
161
|
+
|
|
162
|
+
QueryPurchaseHistoryParams queryPurchaseHistoryParams =
|
|
163
|
+
QueryPurchaseHistoryParams.newBuilder()
|
|
164
|
+
.setProductType(BillingClient.ProductType.SUBS)
|
|
165
|
+
.build();
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
billingClient.queryPurchaseHistoryAsync(queryPurchaseHistoryParams, (BillingResult billingResult, List<PurchaseHistoryRecord> list) -> {
|
|
169
|
+
|
|
170
|
+
// Try to loop through the list until we find a purchase history record associated with the passed in productIdentifier.
|
|
171
|
+
// If we do, then set found to true to break out of the loop, then compile a response with necessary data. Otherwise compile
|
|
172
|
+
// a response saying that the there were not transactions for the given productIdentifier.
|
|
173
|
+
int i = 0;
|
|
174
|
+
boolean found = false;
|
|
175
|
+
while (list != null && (i < list.size() && !found)) {
|
|
176
|
+
try {
|
|
177
|
+
|
|
178
|
+
JSObject currentPurchaseHistoryRecord = new JSObject(list.get(i).getOriginalJson());
|
|
179
|
+
Log.i("PurchaseHistory", currentPurchaseHistoryRecord.toString());
|
|
180
|
+
|
|
181
|
+
if (currentPurchaseHistoryRecord.get("productId").equals(productIdentifier)) {
|
|
182
|
+
|
|
183
|
+
found = true;
|
|
184
|
+
|
|
185
|
+
JSObject data = new JSObject();
|
|
186
|
+
String expiryDate = getExpiryDateFromGoogle(productIdentifier, currentPurchaseHistoryRecord.get("purchaseToken").toString());
|
|
187
|
+
if (expiryDate != null) {
|
|
188
|
+
data.put("expiryDate", expiryDate);
|
|
189
|
+
}
|
|
190
|
+
Calendar calendar = Calendar.getInstance();
|
|
191
|
+
calendar.setTimeInMillis(Long.parseLong((currentPurchaseHistoryRecord.get("purchaseTime").toString())));
|
|
192
|
+
String orderId = currentPurchaseHistoryRecord.optString("orderId", ""); // Usamos optString para obtener un valor por defecto si la clave no existe
|
|
193
|
+
data.put("productIdentifier", currentPurchaseHistoryRecord.get("productId"));
|
|
194
|
+
data.put("originalId", orderId);
|
|
195
|
+
data.put("transactionId", orderId);
|
|
196
|
+
data.put("developerPayload",currentPurchaseHistoryRecord.optString("developerPayload", "")); // Usamos optString para obtener un valor por defecto si la clave no existe
|
|
197
|
+
data.put("purchaseToken", currentPurchaseHistoryRecord.get("purchaseToken").toString());
|
|
198
|
+
|
|
199
|
+
response.put("responseCode", 0);
|
|
200
|
+
response.put("responseMessage", "Successfully found the latest transaction matching given productIdentifier");
|
|
201
|
+
response.put("data", data);
|
|
202
|
+
}
|
|
203
|
+
} catch (Exception e) {
|
|
204
|
+
Logger.error(e.getMessage());
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
i++;
|
|
208
|
+
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// If after looping through the list of purchase history records, no records are found to be associated with
|
|
212
|
+
// the given product identifier, return a response saying no transactions found
|
|
213
|
+
if (!found) {
|
|
214
|
+
response.put("responseCode", 3);
|
|
215
|
+
response.put("responseMessage", "No transaction for given productIdentifier, or it could not be verified");
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
call.resolve(response);
|
|
219
|
+
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
public void getCurrentEntitlements(PluginCall call) {
|
|
227
|
+
|
|
228
|
+
JSObject response = new JSObject();
|
|
229
|
+
|
|
230
|
+
if (billingClientIsConnected == 1) {
|
|
231
|
+
|
|
232
|
+
QueryPurchasesParams queryPurchasesParams =
|
|
233
|
+
QueryPurchasesParams.newBuilder()
|
|
234
|
+
.setProductType(BillingClient.ProductType.SUBS)
|
|
235
|
+
.build();
|
|
236
|
+
|
|
237
|
+
billingClient.queryPurchasesAsync(
|
|
238
|
+
queryPurchasesParams,
|
|
239
|
+
(billingResult, purchaseList) -> {
|
|
240
|
+
|
|
241
|
+
try {
|
|
242
|
+
|
|
243
|
+
int amountOfPurchases = purchaseList.size();
|
|
244
|
+
|
|
245
|
+
if (amountOfPurchases > 0) {
|
|
246
|
+
|
|
247
|
+
ArrayList<JSObject> entitlements = new ArrayList<>();
|
|
248
|
+
for (int i = 0; i < purchaseList.size(); i++) {
|
|
249
|
+
|
|
250
|
+
Purchase currentPurchase = purchaseList.get(i);
|
|
251
|
+
|
|
252
|
+
String expiryDate = this.getExpiryDateFromGoogle(currentPurchase.getProducts().get(0), currentPurchase.getPurchaseToken());
|
|
253
|
+
String orderId = currentPurchase.getOrderId();
|
|
254
|
+
|
|
255
|
+
String dateFormat = "dd-MM-yyyy hh:mm";
|
|
256
|
+
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormat, Locale.getDefault());
|
|
257
|
+
Calendar calendar = Calendar.getInstance();
|
|
258
|
+
calendar.setTimeInMillis(Long.parseLong((String.valueOf(currentPurchase.getPurchaseTime()))));
|
|
259
|
+
|
|
260
|
+
entitlements.add(
|
|
261
|
+
new JSObject()
|
|
262
|
+
.put("productIdentifier", currentPurchase.getProducts().get(0))
|
|
263
|
+
.put("expiryDate", expiryDate)
|
|
264
|
+
.put("originalStartDate", simpleDateFormat.format(calendar.getTime()))
|
|
265
|
+
.put("originalId", orderId)
|
|
266
|
+
.put("transactionId", orderId)
|
|
267
|
+
.put("purchaseToken", currentPurchase.getPurchaseToken())
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
response.put("responseCode", 0);
|
|
272
|
+
response.put("responseMessage", "Successfully found all entitlements across all product types");
|
|
273
|
+
response.put("data", entitlements);
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
} else {
|
|
277
|
+
Log.i("No Purchases", "No active subscriptions found");
|
|
278
|
+
response.put("responseCode", 1);
|
|
279
|
+
response.put("responseMessage", "No entitlements were found");
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
call.resolve(response);
|
|
284
|
+
|
|
285
|
+
} catch (Exception e) {
|
|
286
|
+
Log.e("Error", e.toString());
|
|
287
|
+
response.put("responseCode", 2);
|
|
288
|
+
response.put("responseMessage", e.toString());
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
call.resolve(response);
|
|
292
|
+
|
|
293
|
+
}
|
|
294
|
+
);
|
|
295
|
+
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
public void purchaseProduct(String productIdentifier, String userId, PluginCall call) {
|
|
301
|
+
|
|
302
|
+
JSObject response = new JSObject();
|
|
303
|
+
|
|
304
|
+
if (billingClientIsConnected == 1) {
|
|
305
|
+
|
|
306
|
+
QueryProductDetailsParams.Product productToFind = QueryProductDetailsParams.Product.newBuilder()
|
|
307
|
+
.setProductId(productIdentifier)
|
|
308
|
+
.setProductType(BillingClient.ProductType.SUBS)
|
|
309
|
+
.build();
|
|
310
|
+
|
|
311
|
+
QueryProductDetailsParams queryProductDetailsParams =
|
|
312
|
+
QueryProductDetailsParams.newBuilder()
|
|
313
|
+
.setProductList(List.of(productToFind))
|
|
314
|
+
.build();
|
|
315
|
+
|
|
316
|
+
billingClient.queryProductDetailsAsync(
|
|
317
|
+
queryProductDetailsParams,
|
|
318
|
+
(billingResult1, productDetailsList) -> {
|
|
319
|
+
|
|
320
|
+
try {
|
|
321
|
+
ProductDetails productDetails = productDetailsList.get(0);
|
|
322
|
+
BillingFlowParams.Builder paramsBuilder = BillingFlowParams.newBuilder()
|
|
323
|
+
.setProductDetailsParamsList(
|
|
324
|
+
List.of(
|
|
325
|
+
BillingFlowParams.ProductDetailsParams.newBuilder()
|
|
326
|
+
.setProductDetails(productDetails)
|
|
327
|
+
.setOfferToken(Objects.requireNonNull(productDetails.getSubscriptionOfferDetails()).get(0).getOfferToken())
|
|
328
|
+
.build()
|
|
329
|
+
)
|
|
330
|
+
);
|
|
331
|
+
|
|
332
|
+
if (userId != null && !userId.isEmpty()) {
|
|
333
|
+
paramsBuilder.setObfuscatedAccountId(userId);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
BillingFlowParams billingFlowParams = paramsBuilder.build();
|
|
337
|
+
|
|
338
|
+
BillingResult result = billingClient.launchBillingFlow(this.activity, billingFlowParams);
|
|
339
|
+
|
|
340
|
+
Log.i("RESULT", result.toString());
|
|
341
|
+
response.put("responseCode", 0);
|
|
342
|
+
response.put("responseMessage", "Successfully opened native popover");
|
|
343
|
+
|
|
344
|
+
} catch (Exception e) {
|
|
345
|
+
Logger.error(e.getMessage());
|
|
346
|
+
response.put("responseCode", 1);
|
|
347
|
+
response.put("responseMessage", "Failed to open native popover");
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
call.resolve(response);
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
private String getExpiryDateFromGoogle(String productIdentifier, String purchaseToken) {
|
|
357
|
+
|
|
358
|
+
try {
|
|
359
|
+
|
|
360
|
+
// Compile request to verify purchase token
|
|
361
|
+
URL obj = new URL(this.googleVerifyEndpoint + "?bid=" + this.googleBid + "&subId=" + productIdentifier + "&purchaseToken=" + purchaseToken);
|
|
362
|
+
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
|
|
363
|
+
con.setRequestMethod("GET");
|
|
364
|
+
|
|
365
|
+
// Try to receive response from server
|
|
366
|
+
try (BufferedReader br = new BufferedReader(
|
|
367
|
+
new InputStreamReader(con.getInputStream(), StandardCharsets.UTF_8))) {
|
|
368
|
+
|
|
369
|
+
StringBuilder googleResponse = new StringBuilder();
|
|
370
|
+
String responseLine;
|
|
371
|
+
while ((responseLine = br.readLine()) != null) {
|
|
372
|
+
googleResponse.append(responseLine.trim());
|
|
373
|
+
Log.i("Response Line", responseLine);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// If the response was successful, extract expiryDate and put it in our response data property
|
|
377
|
+
if (con.getResponseCode() == 200) {
|
|
378
|
+
JSObject postResponseJSON = new JSObject(googleResponse.toString());
|
|
379
|
+
JSObject googleResponseJSON = new JSObject(postResponseJSON.get("googleResponse").toString()); // <-- note the typo in response object from server
|
|
380
|
+
JSObject payloadJSON = new JSObject(googleResponseJSON.get("payload").toString());
|
|
381
|
+
String dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";
|
|
382
|
+
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormat, Locale.getDefault());
|
|
383
|
+
Calendar calendar = Calendar.getInstance();
|
|
384
|
+
calendar.setTimeInMillis(Long.parseLong(payloadJSON.get("expiryTimeMillis").toString()));
|
|
385
|
+
return simpleDateFormat.format(calendar.getTime());
|
|
386
|
+
} else {
|
|
387
|
+
return null;
|
|
388
|
+
}
|
|
389
|
+
} catch (Exception e) {
|
|
390
|
+
Logger.error(e.getMessage());
|
|
391
|
+
}
|
|
392
|
+
} catch (Exception e) {
|
|
393
|
+
Logger.error(e.getMessage());
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// If the method manages to each this far before already returning, just return null
|
|
397
|
+
// because something went wrong
|
|
398
|
+
return null;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
package jok.purchases.capacitor;
|
|
2
|
+
|
|
3
|
+
import com.getcapacitor.JSObject;
|
|
4
|
+
import com.getcapacitor.Plugin;
|
|
5
|
+
import com.getcapacitor.PluginCall;
|
|
6
|
+
import com.getcapacitor.PluginMethod;
|
|
7
|
+
import com.getcapacitor.annotation.CapacitorPlugin;
|
|
8
|
+
|
|
9
|
+
import android.content.Intent;
|
|
10
|
+
import android.net.Uri;
|
|
11
|
+
import android.util.Log;
|
|
12
|
+
|
|
13
|
+
import com.android.billingclient.api.AcknowledgePurchaseParams;
|
|
14
|
+
import com.android.billingclient.api.BillingClient;
|
|
15
|
+
import com.android.billingclient.api.Purchase;
|
|
16
|
+
import com.android.billingclient.api.PurchasesUpdatedListener;
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@CapacitorPlugin(name = "Purchases")
|
|
20
|
+
public class PurchasesPlugin extends Plugin {
|
|
21
|
+
|
|
22
|
+
private Subscriptions implementation;
|
|
23
|
+
|
|
24
|
+
private BillingClient billingClient;
|
|
25
|
+
|
|
26
|
+
public PurchasesPlugin () {
|
|
27
|
+
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// This listener is fired upon completing the billing flow, it is vital to call the acknowledgePurchase
|
|
31
|
+
// method on the billingClient, with the purchase token otherwise Google will automatically cancel the subscription
|
|
32
|
+
// shortly after the purchase
|
|
33
|
+
private final PurchasesUpdatedListener purchasesUpdatedListener = (billingResult, purchases) -> {
|
|
34
|
+
|
|
35
|
+
JSObject response = new JSObject();
|
|
36
|
+
|
|
37
|
+
if(purchases != null) {
|
|
38
|
+
for (int i = 0; i < purchases.size(); i++) {
|
|
39
|
+
|
|
40
|
+
Purchase currentPurchase = purchases.get(i);
|
|
41
|
+
if (!currentPurchase.isAcknowledged() && billingResult.getResponseCode() == 0 && currentPurchase.getPurchaseState() != 2) {
|
|
42
|
+
|
|
43
|
+
AcknowledgePurchaseParams acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder()
|
|
44
|
+
.setPurchaseToken(currentPurchase.getPurchaseToken())
|
|
45
|
+
.build();
|
|
46
|
+
|
|
47
|
+
billingClient.acknowledgePurchase(acknowledgePurchaseParams, billingResult1 -> {
|
|
48
|
+
Log.i("Purchase ack", currentPurchase.getOriginalJson());
|
|
49
|
+
billingResult1.getResponseCode();
|
|
50
|
+
|
|
51
|
+
response.put("successful", billingResult1.getResponseCode());
|
|
52
|
+
|
|
53
|
+
// WARNING: Changed the notifyListeners method from protected to public in order to get the method call to work
|
|
54
|
+
// This may be a security issue in the future - in order to fix it, it may be best to move this listener + the billingClient
|
|
55
|
+
// initiation into the SubscriptionsPlugin.java, then pass it into this implementation class so we can still access the
|
|
56
|
+
// billingClient.
|
|
57
|
+
notifyListeners("ANDROID-PURCHASE-RESPONSE", response);
|
|
58
|
+
});
|
|
59
|
+
} else {
|
|
60
|
+
response.put("successful", false);
|
|
61
|
+
notifyListeners("ANDROID-PURCHASE-RESPONSE", response);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
}
|
|
65
|
+
} else {
|
|
66
|
+
response.put("successful", false);
|
|
67
|
+
notifyListeners("ANDROID-PURCHASE-RESPONSE", response);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
@Override
|
|
73
|
+
public void load() {
|
|
74
|
+
|
|
75
|
+
this.billingClient = BillingClient.newBuilder(getContext())
|
|
76
|
+
.setListener(purchasesUpdatedListener)
|
|
77
|
+
.enablePendingPurchases()
|
|
78
|
+
.build();
|
|
79
|
+
implementation = new Subscriptions(this, billingClient);
|
|
80
|
+
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
@PluginMethod
|
|
84
|
+
public void setGoogleVerificationDetails(PluginCall call) {
|
|
85
|
+
String googleVerifyEndpoint = call.getString("googleVerifyEndpoint");
|
|
86
|
+
String bid = call.getString("bid");
|
|
87
|
+
|
|
88
|
+
if(googleVerifyEndpoint != null && bid != null) {
|
|
89
|
+
implementation.setGoogleVerificationDetails(googleVerifyEndpoint, bid);
|
|
90
|
+
} else {
|
|
91
|
+
call.reject("Missing required parameters");
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
@PluginMethod
|
|
96
|
+
public void echo(PluginCall call) {
|
|
97
|
+
String value = call.getString("value");
|
|
98
|
+
|
|
99
|
+
JSObject ret = new JSObject();
|
|
100
|
+
ret.put("value", implementation.echo(value));
|
|
101
|
+
call.resolve(ret);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
@PluginMethod
|
|
105
|
+
public void getProductDetails(PluginCall call) {
|
|
106
|
+
|
|
107
|
+
String productIdentifier = call.getString("productIdentifier");
|
|
108
|
+
|
|
109
|
+
if (productIdentifier == null) {
|
|
110
|
+
call.reject("Must provide a productID");
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
implementation.getProductDetails(productIdentifier, call);
|
|
114
|
+
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
@PluginMethod
|
|
118
|
+
public void purchaseProduct(PluginCall call) {
|
|
119
|
+
|
|
120
|
+
String productIdentifier = call.getString("productIdentifier");
|
|
121
|
+
String userId = call.getString("userId", "");
|
|
122
|
+
|
|
123
|
+
if(productIdentifier == null) {
|
|
124
|
+
call.reject("Must provide a productID");
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
implementation.purchaseProduct(productIdentifier, userId, call);
|
|
128
|
+
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
@PluginMethod
|
|
132
|
+
public void getLatestTransaction(PluginCall call) {
|
|
133
|
+
|
|
134
|
+
String productIdentifier = call.getString("productIdentifier");
|
|
135
|
+
|
|
136
|
+
if(productIdentifier == null) {
|
|
137
|
+
call.reject("Must provide a productID");
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
implementation.getLatestTransaction(productIdentifier, call);
|
|
141
|
+
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
@PluginMethod
|
|
145
|
+
public void getCurrentEntitlements(PluginCall call) {
|
|
146
|
+
|
|
147
|
+
implementation.getCurrentEntitlements(call);
|
|
148
|
+
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
@PluginMethod
|
|
152
|
+
public void manageSubscriptions(PluginCall call) {
|
|
153
|
+
|
|
154
|
+
String productIdentifier = call.getString("productIdentifier");
|
|
155
|
+
String bid = call.getString("bid");
|
|
156
|
+
|
|
157
|
+
if(productIdentifier == null) {
|
|
158
|
+
call.reject("Must provide a productID");
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if(bid == null) {
|
|
162
|
+
call.reject("Must provide a bundleID");
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
Intent browserIntent = new Intent(Intent.ACTION_VIEW,
|
|
166
|
+
Uri.parse("https://play.google.com/store/account/subscriptions?sku=" + productIdentifier + "&package=" + bid));
|
|
167
|
+
getActivity().startActivity(browserIntent);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
File without changes
|