@capgo/native-purchases 0.0.30 → 0.0.33
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.
|
@@ -27,11 +27,17 @@ import java.util.List;
|
|
|
27
27
|
import java.util.concurrent.CountDownLatch;
|
|
28
28
|
import org.json.JSONArray;
|
|
29
29
|
import org.json.JSONException;
|
|
30
|
+
import java.util.concurrent.TimeoutException;
|
|
31
|
+
import java.util.concurrent.Phaser;
|
|
32
|
+
import java.util.concurrent.Semaphore;
|
|
33
|
+
import java.util.concurrent.TimeUnit;
|
|
30
34
|
|
|
31
35
|
@CapacitorPlugin(name = "NativePurchases")
|
|
32
36
|
public class NativePurchasesPlugin extends Plugin {
|
|
33
37
|
|
|
34
38
|
public final String PLUGIN_VERSION = "0.0.25";
|
|
39
|
+
public static final String TAG = "NativePurchases";
|
|
40
|
+
private static final Phaser semaphoreReady = new Phaser(1);
|
|
35
41
|
private BillingClient billingClient;
|
|
36
42
|
|
|
37
43
|
private void isBillingSupported(PluginCall call) {
|
|
@@ -40,6 +46,59 @@ public class NativePurchasesPlugin extends Plugin {
|
|
|
40
46
|
call.resolve();
|
|
41
47
|
}
|
|
42
48
|
|
|
49
|
+
@Override
|
|
50
|
+
public void load() {
|
|
51
|
+
super.load();
|
|
52
|
+
Log.i(NativePurchasesPlugin.TAG, "load");
|
|
53
|
+
semaphoreDown();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
private void semaphoreWait(Number waitTime) {
|
|
57
|
+
Log.i(NativePurchasesPlugin.TAG, "semaphoreWait " + waitTime);
|
|
58
|
+
try {
|
|
59
|
+
// Log.i(CapacitorUpdater.TAG, "semaphoreReady count " + CapacitorUpdaterPlugin.this.semaphoreReady.getCount());
|
|
60
|
+
NativePurchasesPlugin.this.semaphoreReady.awaitAdvanceInterruptibly(
|
|
61
|
+
NativePurchasesPlugin.this.semaphoreReady.getPhase(),
|
|
62
|
+
waitTime.longValue(),
|
|
63
|
+
TimeUnit.SECONDS
|
|
64
|
+
);
|
|
65
|
+
// Log.i(CapacitorUpdater.TAG, "semaphoreReady await " + res);
|
|
66
|
+
Log.i(
|
|
67
|
+
NativePurchasesPlugin.TAG,
|
|
68
|
+
"semaphoreReady count " +
|
|
69
|
+
NativePurchasesPlugin.this.semaphoreReady.getPhase()
|
|
70
|
+
);
|
|
71
|
+
} catch (InterruptedException e) {
|
|
72
|
+
Log.i(NativePurchasesPlugin.TAG, "semaphoreWait InterruptedException");
|
|
73
|
+
e.printStackTrace();
|
|
74
|
+
} catch (TimeoutException e) {
|
|
75
|
+
throw new RuntimeException(e);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
private void semaphoreUp() {
|
|
80
|
+
Log.i(NativePurchasesPlugin.TAG, "semaphoreUp");
|
|
81
|
+
NativePurchasesPlugin.this.semaphoreReady.register();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
private void semaphoreDown() {
|
|
85
|
+
Log.i(NativePurchasesPlugin.TAG, "semaphoreDown");
|
|
86
|
+
Log.i(
|
|
87
|
+
NativePurchasesPlugin.TAG,
|
|
88
|
+
"semaphoreDown count " +
|
|
89
|
+
NativePurchasesPlugin.this.semaphoreReady.getPhase()
|
|
90
|
+
);
|
|
91
|
+
NativePurchasesPlugin.this.semaphoreReady.arriveAndDeregister();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
private void closeBillingClient() {
|
|
95
|
+
if (billingClient != null) {
|
|
96
|
+
billingClient.endConnection();
|
|
97
|
+
billingClient = null;
|
|
98
|
+
semaphoreDown();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
43
102
|
private void handlePurchase(Purchase purchase, PluginCall purchaseCall) {
|
|
44
103
|
if (purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED) {
|
|
45
104
|
// Grant entitlement to the user, then acknowledge the purchase
|
|
@@ -78,7 +137,7 @@ public class NativePurchasesPlugin extends Plugin {
|
|
|
78
137
|
public void onAcknowledgePurchaseResponse(BillingResult billingResult) {
|
|
79
138
|
// Handle the result of the acknowledge purchase
|
|
80
139
|
Log.i(
|
|
81
|
-
|
|
140
|
+
NativePurchasesPlugin.TAG,
|
|
82
141
|
"onAcknowledgePurchaseResponse" + billingResult
|
|
83
142
|
);
|
|
84
143
|
}
|
|
@@ -87,10 +146,9 @@ public class NativePurchasesPlugin extends Plugin {
|
|
|
87
146
|
}
|
|
88
147
|
|
|
89
148
|
private void initBillingClient(PluginCall purchaseCall) {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
149
|
+
semaphoreWait(10);
|
|
150
|
+
closeBillingClient();
|
|
151
|
+
semaphoreUp();
|
|
94
152
|
CountDownLatch semaphoreReady = new CountDownLatch(1);
|
|
95
153
|
billingClient =
|
|
96
154
|
BillingClient
|
|
@@ -102,7 +160,7 @@ public class NativePurchasesPlugin extends Plugin {
|
|
|
102
160
|
BillingResult billingResult,
|
|
103
161
|
List<Purchase> purchases
|
|
104
162
|
) {
|
|
105
|
-
Log.i(
|
|
163
|
+
Log.i(NativePurchasesPlugin.TAG, "onPurchasesUpdated" + billingResult);
|
|
106
164
|
if (
|
|
107
165
|
billingResult.getResponseCode() ==
|
|
108
166
|
BillingClient.BillingResponseCode.OK &&
|
|
@@ -114,13 +172,12 @@ public class NativePurchasesPlugin extends Plugin {
|
|
|
114
172
|
handlePurchase(purchases.get(0), purchaseCall);
|
|
115
173
|
} else {
|
|
116
174
|
// Handle any other error codes.
|
|
117
|
-
Log.i(
|
|
175
|
+
Log.i(NativePurchasesPlugin.TAG, "onPurchasesUpdated" + billingResult);
|
|
118
176
|
if (purchaseCall != null) {
|
|
119
177
|
purchaseCall.reject("Purchase is not purchased");
|
|
120
178
|
}
|
|
121
179
|
}
|
|
122
|
-
|
|
123
|
-
billingClient = null;
|
|
180
|
+
closeBillingClient();
|
|
124
181
|
return;
|
|
125
182
|
}
|
|
126
183
|
}
|
|
@@ -209,66 +266,70 @@ public class NativePurchasesPlugin extends Plugin {
|
|
|
209
266
|
.setProductList(productList)
|
|
210
267
|
.build();
|
|
211
268
|
this.initBillingClient(call);
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
269
|
+
try {
|
|
270
|
+
billingClient.queryProductDetailsAsync(
|
|
271
|
+
params,
|
|
272
|
+
new ProductDetailsResponseListener() {
|
|
273
|
+
public void onProductDetailsResponse(
|
|
274
|
+
BillingResult billingResult,
|
|
275
|
+
List<ProductDetails> productDetailsList
|
|
276
|
+
) {
|
|
277
|
+
if (productDetailsList.size() == 0) {
|
|
278
|
+
closeBillingClient();
|
|
279
|
+
call.reject("Product not found");
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
// Process the result
|
|
283
|
+
List<BillingFlowParams.ProductDetailsParams> productDetailsParamsList = new ArrayList<>();
|
|
284
|
+
for (ProductDetails productDetailsItem : productDetailsList) {
|
|
285
|
+
BillingFlowParams.ProductDetailsParams.Builder productDetailsParams = BillingFlowParams.ProductDetailsParams
|
|
286
|
+
.newBuilder()
|
|
287
|
+
.setProductDetails(productDetailsItem);
|
|
288
|
+
if (productType.equals("subs")) {
|
|
289
|
+
// list the SubscriptionOfferDetails and find the one who match the planIdentifier if not found get the first one
|
|
290
|
+
ProductDetails.SubscriptionOfferDetails selectedOfferDetails =
|
|
291
|
+
null;
|
|
292
|
+
for (ProductDetails.SubscriptionOfferDetails offerDetails : productDetailsItem.getSubscriptionOfferDetails()) {
|
|
293
|
+
if (offerDetails.getOfferId().equals(planIdentifier)) {
|
|
294
|
+
selectedOfferDetails = offerDetails;
|
|
295
|
+
break;
|
|
296
|
+
}
|
|
239
297
|
}
|
|
298
|
+
if (selectedOfferDetails == null) {
|
|
299
|
+
selectedOfferDetails =
|
|
300
|
+
productDetailsItem.getSubscriptionOfferDetails().get(0);
|
|
301
|
+
}
|
|
302
|
+
productDetailsParams.setOfferToken(
|
|
303
|
+
selectedOfferDetails.getOfferToken()
|
|
304
|
+
);
|
|
240
305
|
}
|
|
241
|
-
|
|
242
|
-
selectedOfferDetails =
|
|
243
|
-
productDetailsItem.getSubscriptionOfferDetails().get(0);
|
|
244
|
-
}
|
|
245
|
-
productDetailsParams.setOfferToken(
|
|
246
|
-
selectedOfferDetails.getOfferToken()
|
|
247
|
-
);
|
|
306
|
+
productDetailsParamsList.add(productDetailsParams.build());
|
|
248
307
|
}
|
|
249
|
-
|
|
308
|
+
BillingFlowParams billingFlowParams = BillingFlowParams
|
|
309
|
+
.newBuilder()
|
|
310
|
+
.setProductDetailsParamsList(productDetailsParamsList)
|
|
311
|
+
.build();
|
|
312
|
+
// Launch the billing flow
|
|
313
|
+
BillingResult billingResult2 = billingClient.launchBillingFlow(
|
|
314
|
+
getActivity(),
|
|
315
|
+
billingFlowParams
|
|
316
|
+
);
|
|
317
|
+
Log.i(
|
|
318
|
+
NativePurchasesPlugin.TAG,
|
|
319
|
+
"onProductDetailsResponse2" + billingResult2
|
|
320
|
+
);
|
|
250
321
|
}
|
|
251
|
-
BillingFlowParams billingFlowParams = BillingFlowParams
|
|
252
|
-
.newBuilder()
|
|
253
|
-
.setProductDetailsParamsList(productDetailsParamsList)
|
|
254
|
-
.build();
|
|
255
|
-
// Launch the billing flow
|
|
256
|
-
BillingResult billingResult2 = billingClient.launchBillingFlow(
|
|
257
|
-
getActivity(),
|
|
258
|
-
billingFlowParams
|
|
259
|
-
);
|
|
260
|
-
Log.i(
|
|
261
|
-
"NativePurchases",
|
|
262
|
-
"onProductDetailsResponse2" + billingResult2
|
|
263
|
-
);
|
|
264
322
|
}
|
|
265
|
-
|
|
266
|
-
)
|
|
323
|
+
);
|
|
324
|
+
} catch (Exception e) {
|
|
325
|
+
closeBillingClient();
|
|
326
|
+
call.reject(e.getMessage());
|
|
327
|
+
}
|
|
267
328
|
}
|
|
268
329
|
|
|
269
330
|
@PluginMethod
|
|
270
331
|
public void restorePurchases(PluginCall call) {
|
|
271
|
-
Log.d(
|
|
332
|
+
Log.d(NativePurchasesPlugin.TAG, "restorePurchases");
|
|
272
333
|
this.initBillingClient(null);
|
|
273
334
|
call.resolve();
|
|
274
335
|
}
|
|
@@ -283,7 +344,9 @@ public class NativePurchasesPlugin extends Plugin {
|
|
|
283
344
|
call.reject("planIdentifier cannot be empty if productType is subs");
|
|
284
345
|
return;
|
|
285
346
|
}
|
|
286
|
-
if (
|
|
347
|
+
if (
|
|
348
|
+
productIdentifiersArray == null || productIdentifiersArray.length() == 0
|
|
349
|
+
) {
|
|
287
350
|
call.reject("productIdentifiers array missing");
|
|
288
351
|
return;
|
|
289
352
|
}
|
|
@@ -297,7 +360,7 @@ public class NativePurchasesPlugin extends Plugin {
|
|
|
297
360
|
}
|
|
298
361
|
}
|
|
299
362
|
|
|
300
|
-
Log.d(
|
|
363
|
+
Log.d(NativePurchasesPlugin.TAG, "getProducts: " + productIdentifiers);
|
|
301
364
|
List<QueryProductDetailsParams.Product> productList = new ArrayList<>();
|
|
302
365
|
for (String productIdentifier : productIdentifiers) {
|
|
303
366
|
productList.add(
|
|
@@ -319,109 +382,114 @@ public class NativePurchasesPlugin extends Plugin {
|
|
|
319
382
|
.setProductList(productList)
|
|
320
383
|
.build();
|
|
321
384
|
this.initBillingClient(call);
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
385
|
+
try {
|
|
386
|
+
billingClient.queryProductDetailsAsync(
|
|
387
|
+
params,
|
|
388
|
+
new ProductDetailsResponseListener() {
|
|
389
|
+
public void onProductDetailsResponse(
|
|
390
|
+
BillingResult billingResult,
|
|
391
|
+
List<ProductDetails> productDetailsList
|
|
392
|
+
) {
|
|
393
|
+
if (productDetailsList.size() == 0) {
|
|
394
|
+
closeBillingClient();
|
|
395
|
+
call.reject("Product not found");
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
Log.i(
|
|
399
|
+
NativePurchasesPlugin.TAG,
|
|
400
|
+
"onProductDetailsResponse" + billingResult + productDetailsList
|
|
401
|
+
);
|
|
402
|
+
// Process the result
|
|
403
|
+
JSObject ret = new JSObject();
|
|
404
|
+
JSONArray products = new JSONArray();
|
|
405
|
+
Number productIdIndex = 0;
|
|
406
|
+
for (ProductDetails productDetails : productDetailsList) {
|
|
407
|
+
JSObject product = new JSObject();
|
|
408
|
+
product.put("identifier", productDetails.getProductId());
|
|
409
|
+
product.put("title", productDetails.getTitle());
|
|
410
|
+
product.put("description", productDetails.getDescription());
|
|
411
|
+
if (productType.equals("inapp")) {
|
|
412
|
+
product.put(
|
|
413
|
+
"price",
|
|
414
|
+
productDetails
|
|
415
|
+
.getOneTimePurchaseOfferDetails()
|
|
416
|
+
.getPriceAmountMicros() /
|
|
417
|
+
1000000.0
|
|
418
|
+
);
|
|
419
|
+
product.put(
|
|
420
|
+
"priceString",
|
|
421
|
+
productDetails
|
|
422
|
+
.getOneTimePurchaseOfferDetails()
|
|
423
|
+
.getFormattedPrice()
|
|
424
|
+
);
|
|
425
|
+
product.put(
|
|
426
|
+
"currencyCode",
|
|
427
|
+
productDetails
|
|
428
|
+
.getOneTimePurchaseOfferDetails()
|
|
429
|
+
.getPriceCurrencyCode()
|
|
430
|
+
);
|
|
431
|
+
} else {
|
|
432
|
+
// productIdIndex is used to get the correct SubscriptionOfferDetails by productIdentifiersArray index and increment it
|
|
433
|
+
String subscriptionOfferIdentifier = productIdentifiers.get(
|
|
434
|
+
productIdIndex.intValue()
|
|
435
|
+
);
|
|
436
|
+
productIdIndex = productIdIndex.intValue() + 1;
|
|
437
|
+
// get the SubscriptionOfferDetails who match the subscriptionOfferIdentifier
|
|
438
|
+
ProductDetails.SubscriptionOfferDetails selectedOfferDetails =
|
|
439
|
+
null;
|
|
440
|
+
for (ProductDetails.SubscriptionOfferDetails offerDetails : productDetails.getSubscriptionOfferDetails()) {
|
|
441
|
+
if (
|
|
442
|
+
offerDetails
|
|
443
|
+
.getOfferId()
|
|
444
|
+
.equals(subscriptionOfferIdentifier)
|
|
445
|
+
) {
|
|
446
|
+
selectedOfferDetails = offerDetails;
|
|
447
|
+
break;
|
|
448
|
+
}
|
|
383
449
|
}
|
|
450
|
+
if (selectedOfferDetails == null) {
|
|
451
|
+
selectedOfferDetails =
|
|
452
|
+
productDetails.getSubscriptionOfferDetails().get(0);
|
|
453
|
+
}
|
|
454
|
+
product.put(
|
|
455
|
+
"price",
|
|
456
|
+
selectedOfferDetails
|
|
457
|
+
.getPricingPhases()
|
|
458
|
+
.getPricingPhaseList()
|
|
459
|
+
.get(0)
|
|
460
|
+
.getPriceAmountMicros() /
|
|
461
|
+
1000000.0
|
|
462
|
+
);
|
|
463
|
+
product.put(
|
|
464
|
+
"priceString",
|
|
465
|
+
selectedOfferDetails
|
|
466
|
+
.getPricingPhases()
|
|
467
|
+
.getPricingPhaseList()
|
|
468
|
+
.get(0)
|
|
469
|
+
.getFormattedPrice()
|
|
470
|
+
);
|
|
471
|
+
product.put(
|
|
472
|
+
"currencyCode",
|
|
473
|
+
selectedOfferDetails
|
|
474
|
+
.getPricingPhases()
|
|
475
|
+
.getPricingPhaseList()
|
|
476
|
+
.get(0)
|
|
477
|
+
.getPriceCurrencyCode()
|
|
478
|
+
);
|
|
384
479
|
}
|
|
385
|
-
if (selectedOfferDetails == null) {
|
|
386
|
-
selectedOfferDetails =
|
|
387
|
-
productDetails.getSubscriptionOfferDetails().get(0);
|
|
388
|
-
}
|
|
389
|
-
product.put(
|
|
390
|
-
"price",
|
|
391
|
-
selectedOfferDetails
|
|
392
|
-
.getPricingPhases()
|
|
393
|
-
.getPricingPhaseList()
|
|
394
|
-
.get(0)
|
|
395
|
-
.getPriceAmountMicros() /
|
|
396
|
-
1000000.0
|
|
397
|
-
);
|
|
398
|
-
product.put(
|
|
399
|
-
"priceString",
|
|
400
|
-
selectedOfferDetails
|
|
401
|
-
.getPricingPhases()
|
|
402
|
-
.getPricingPhaseList()
|
|
403
|
-
.get(0)
|
|
404
|
-
.getFormattedPrice()
|
|
405
|
-
);
|
|
406
|
-
product.put(
|
|
407
|
-
"currencyCode",
|
|
408
|
-
selectedOfferDetails
|
|
409
|
-
.getPricingPhases()
|
|
410
|
-
.getPricingPhaseList()
|
|
411
|
-
.get(0)
|
|
412
|
-
.getPriceCurrencyCode()
|
|
413
|
-
);
|
|
414
|
-
}
|
|
415
480
|
|
|
416
|
-
|
|
417
|
-
|
|
481
|
+
product.put("isFamilyShareable", false);
|
|
482
|
+
products.put(product);
|
|
483
|
+
}
|
|
484
|
+
ret.put("products", products);
|
|
485
|
+
closeBillingClient();
|
|
486
|
+
call.resolve(ret);
|
|
418
487
|
}
|
|
419
|
-
ret.put("products", products);
|
|
420
|
-
billingClient.endConnection();
|
|
421
|
-
billingClient = null;
|
|
422
|
-
call.resolve(ret);
|
|
423
488
|
}
|
|
424
|
-
|
|
425
|
-
)
|
|
489
|
+
);
|
|
490
|
+
} catch (Exception e) {
|
|
491
|
+
closeBillingClient();
|
|
492
|
+
call.reject(e.getMessage());
|
|
493
|
+
}
|
|
426
494
|
}
|
|
427
495
|
}
|
|
@@ -82,6 +82,10 @@ public class NativePurchasesPlugin: CAPPlugin {
|
|
|
82
82
|
Task {
|
|
83
83
|
do {
|
|
84
84
|
try await AppStore.sync()
|
|
85
|
+
// make finish() calls for all transactions
|
|
86
|
+
// for transaction in AppStore.transactions {
|
|
87
|
+
// await transaction.finish()
|
|
88
|
+
// }
|
|
85
89
|
call.resolve()
|
|
86
90
|
} catch {
|
|
87
91
|
call.reject(error.localizedDescription)
|