@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
- "NativePurchases",
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
- if (billingClient != null) {
91
- billingClient.endConnection();
92
- billingClient = null;
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("NativePurchases", "onPurchasesUpdated" + billingResult);
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("NativePurchases", "onPurchasesUpdated" + billingResult);
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
- billingClient.endConnection();
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
- billingClient.queryProductDetailsAsync(
213
- params,
214
- new ProductDetailsResponseListener() {
215
- public void onProductDetailsResponse(
216
- BillingResult billingResult,
217
- List<ProductDetails> productDetailsList
218
- ) {
219
- if (productDetailsList.size() == 0) {
220
- billingClient.endConnection();
221
- billingClient = null;
222
- call.reject("Product not found");
223
- return;
224
- }
225
- // Process the result
226
- List<BillingFlowParams.ProductDetailsParams> productDetailsParamsList = new ArrayList<>();
227
- for (ProductDetails productDetailsItem : productDetailsList) {
228
- BillingFlowParams.ProductDetailsParams.Builder productDetailsParams = BillingFlowParams.ProductDetailsParams
229
- .newBuilder()
230
- .setProductDetails(productDetailsItem);
231
- if (productType.equals("subs")) {
232
- // list the SubscriptionOfferDetails and find the one who match the planIdentifier if not found get the first one
233
- ProductDetails.SubscriptionOfferDetails selectedOfferDetails =
234
- null;
235
- for (ProductDetails.SubscriptionOfferDetails offerDetails : productDetailsItem.getSubscriptionOfferDetails()) {
236
- if (offerDetails.getOfferId().equals(planIdentifier)) {
237
- selectedOfferDetails = offerDetails;
238
- break;
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
- if (selectedOfferDetails == null) {
242
- selectedOfferDetails =
243
- productDetailsItem.getSubscriptionOfferDetails().get(0);
244
- }
245
- productDetailsParams.setOfferToken(
246
- selectedOfferDetails.getOfferToken()
247
- );
306
+ productDetailsParamsList.add(productDetailsParams.build());
248
307
  }
249
- productDetailsParamsList.add(productDetailsParams.build());
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("NativePurchases", "restorePurchases");
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 (productIdentifiersArray == null || productIdentifiersArray.length() == 0) {
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("NativePurchases", "getProducts: " + productIdentifiers);
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
- billingClient.queryProductDetailsAsync(
323
- params,
324
- new ProductDetailsResponseListener() {
325
- public void onProductDetailsResponse(
326
- BillingResult billingResult,
327
- List<ProductDetails> productDetailsList
328
- ) {
329
- if (productDetailsList.size() == 0) {
330
- billingClient.endConnection();
331
- billingClient = null;
332
- call.reject("Product not found");
333
- return;
334
- }
335
- Log.i(
336
- "NativePurchases",
337
- "onProductDetailsResponse" + billingResult + productDetailsList
338
- );
339
- // Process the result
340
- JSObject ret = new JSObject();
341
- JSONArray products = new JSONArray();
342
- Number productIdIndex = 0;
343
- for (ProductDetails productDetails : productDetailsList) {
344
- JSObject product = new JSObject();
345
- product.put("identifier", productDetails.getProductId());
346
- product.put("title", productDetails.getTitle());
347
- product.put("description", productDetails.getDescription());
348
- if (productType.equals("inapp")) {
349
- product.put(
350
- "price",
351
- productDetails
352
- .getOneTimePurchaseOfferDetails()
353
- .getPriceAmountMicros() /
354
- 1000000.0
355
- );
356
- product.put(
357
- "priceString",
358
- productDetails
359
- .getOneTimePurchaseOfferDetails()
360
- .getFormattedPrice()
361
- );
362
- product.put(
363
- "currencyCode",
364
- productDetails
365
- .getOneTimePurchaseOfferDetails()
366
- .getPriceCurrencyCode()
367
- );
368
- } else {
369
- // productIdIndex is used to get the correct SubscriptionOfferDetails by productIdentifiersArray index and increment it
370
- String subscriptionOfferIdentifier = productIdentifiers.get(
371
- productIdIndex.intValue()
372
- );
373
- productIdIndex = productIdIndex.intValue() + 1;
374
- // get the SubscriptionOfferDetails who match the subscriptionOfferIdentifier
375
- ProductDetails.SubscriptionOfferDetails selectedOfferDetails =
376
- null;
377
- for (ProductDetails.SubscriptionOfferDetails offerDetails : productDetails.getSubscriptionOfferDetails()) {
378
- if (
379
- offerDetails.getOfferId().equals(subscriptionOfferIdentifier)
380
- ) {
381
- selectedOfferDetails = offerDetails;
382
- break;
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
- product.put("isFamilyShareable", false);
417
- products.put(product);
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)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capgo/native-purchases",
3
- "version": "0.0.30",
3
+ "version": "0.0.33",
4
4
  "description": "In-app Subscriptions Made Easy",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",