sjbillingclient 0.2.2__tar.gz → 1.0.0__tar.gz

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.
Files changed (21) hide show
  1. {sjbillingclient-0.2.2 → sjbillingclient-1.0.0}/PKG-INFO +125 -7
  2. {sjbillingclient-0.2.2 → sjbillingclient-1.0.0}/README.md +124 -6
  3. {sjbillingclient-0.2.2 → sjbillingclient-1.0.0}/pyproject.toml +1 -1
  4. sjbillingclient-1.0.0/sjbillingclient/jclass/accountidentifiers.py +10 -0
  5. {sjbillingclient-0.2.2 → sjbillingclient-1.0.0}/sjbillingclient/jclass/acknowledge.py +1 -1
  6. {sjbillingclient-0.2.2 → sjbillingclient-1.0.0}/sjbillingclient/jclass/billing.py +58 -18
  7. {sjbillingclient-0.2.2 → sjbillingclient-1.0.0}/sjbillingclient/jclass/consume.py +1 -1
  8. sjbillingclient-1.0.0/sjbillingclient/jclass/purchase.py +54 -0
  9. sjbillingclient-1.0.0/sjbillingclient/jclass/queryproduct.py +36 -0
  10. sjbillingclient-1.0.0/sjbillingclient/jclass/querypurchases.py +14 -0
  11. {sjbillingclient-0.2.2 → sjbillingclient-1.0.0}/sjbillingclient/jinterface/purchases.py +13 -1
  12. {sjbillingclient-0.2.2 → sjbillingclient-1.0.0}/sjbillingclient/tools/__init__.py +77 -9
  13. sjbillingclient-0.2.2/sjbillingclient/jclass/purchase.py +0 -16
  14. sjbillingclient-0.2.2/sjbillingclient/jclass/queryproduct.py +0 -21
  15. {sjbillingclient-0.2.2 → sjbillingclient-1.0.0}/sjbillingclient/__init__.py +0 -0
  16. {sjbillingclient-0.2.2 → sjbillingclient-1.0.0}/sjbillingclient/jclass/__init__.py +0 -0
  17. {sjbillingclient-0.2.2 → sjbillingclient-1.0.0}/sjbillingclient/jinterface/__init__.py +0 -0
  18. {sjbillingclient-0.2.2 → sjbillingclient-1.0.0}/sjbillingclient/jinterface/acknowledge.py +0 -0
  19. {sjbillingclient-0.2.2 → sjbillingclient-1.0.0}/sjbillingclient/jinterface/billing.py +0 -0
  20. {sjbillingclient-0.2.2 → sjbillingclient-1.0.0}/sjbillingclient/jinterface/consume.py +0 -0
  21. {sjbillingclient-0.2.2 → sjbillingclient-1.0.0}/sjbillingclient/jinterface/product.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sjbillingclient
3
- Version: 0.2.2
3
+ Version: 1.0.0
4
4
  Summary: A Python wrapper for the Google Play Billing Library that facilitates in-app purchases and subscriptions in Android applications
5
5
  Home-page: https://github.com/SimpleJnius/sj-android-billingclient
6
6
  Author: Kenechukwu Akubue
@@ -21,10 +21,6 @@ Description-Content-Type: text/markdown
21
21
 
22
22
  # SJBillingClient (Google Play Billing SDK for Python)
23
23
 
24
- <!-- GitAds-Verify: 71CCWMQSMVD67LS4WF4N44EXISSL2UTQ -->
25
- ## GitAds Sponsored
26
- [![Sponsored by GitAds](https://gitads.dev/v1/ad-serve?source=simplejnius/sj-android-billingclient@github)](https://gitads.dev/v1/ad-track?source=simplejnius/sj-android-billingclient@github)
27
-
28
24
  ## Overview
29
25
 
30
26
  SJBillingClient is a Python wrapper for the Google Play Billing Library that facilitates in-app purchases and subscriptions in Android applications. It provides a high-level, Pythonic interface to interact with Google Play's billing system, making it easier to implement and manage in-app purchases in Python-based Android apps (like those built with Kivy/Python-for-Android).
@@ -41,14 +37,18 @@ SJBillingClient is a Python wrapper for the Google Play Billing Library that fac
41
37
 
42
38
  - Python 3.9+
43
39
  - pyjnius 1.6.1+
44
- - Android application with Google Play Billing Library (version 8.0.0 recommended)
40
+ - Android application with Google Play Billing Library (version 8.0.0 required)
41
+
42
+ > **Note**: This library is specifically designed for Google Play Billing Library version 8.0.0. Earlier or later versions may not be compatible due to API changes.
45
43
 
46
44
  ## Installation
47
45
 
48
46
  ```shell
49
47
  # Using pip
50
48
  pip install sjbillingclient
49
+ ````
51
50
 
51
+ ```rpmspec
52
52
  # In Buildozer (add to buildozer.spec)
53
53
  requirements = sjbillingclient
54
54
  android.gradle_dependencies = com.android.billingclient:billing:8.0.0
@@ -161,6 +161,37 @@ def on_acknowledge_purchase_response(billing_result):
161
161
  client.acknowledge_purchase(purchase.getPurchaseToken(), on_acknowledge_purchase_response)
162
162
  ```
163
163
 
164
+ ### Querying Purchases
165
+
166
+ ```python
167
+ from sjbillingclient.tools import BillingClient
168
+ from sjbillingclient.jclass.billing import ProductType, BillingResponseCode
169
+ from sjbillingclient.jclass.purchase import PurchaseState
170
+
171
+ def on_query_purchases_response(billing_result, purchases):
172
+ if billing_result.getResponseCode() == BillingResponseCode.OK:
173
+ if purchases and not purchases.isEmpty():
174
+ for i in range(purchases.size()):
175
+ purchase = purchases.get(i)
176
+ # Get formatted purchase details
177
+ purchase_details = client.get_purchase(purchase)
178
+ print(f"Products: {purchase_details['products']}")
179
+ print(f"Purchase token: {purchase_details['purchase_token']}")
180
+ print(f"Purchase state: {purchase_details['purchase_state']}")
181
+
182
+ # Handle the purchase based on its state
183
+ if purchase_details['purchase_state'] == PurchaseState.PURCHASED:
184
+ if not purchase_details['is_acknowledged']:
185
+ # Acknowledge the purchase
186
+ client.acknowledge_purchase(purchase.getPurchaseToken(), on_acknowledge_purchase_response)
187
+
188
+ # Query purchases for a specific product type
189
+ client.query_purchase_async(
190
+ product_type=ProductType.INAPP,
191
+ on_query_purchases_response=on_query_purchases_response
192
+ )
193
+ ```
194
+
164
195
  ### Kivy Integration Example
165
196
 
166
197
  Here's a complete example of integrating SJBillingClient with a Kivy application:
@@ -284,7 +315,7 @@ class HomeScreen(Screen):
284
315
  product_id = self.ids.btn.product_id
285
316
  if billing_result.getResponseCode() == BillingResponseCode.OK:
286
317
  self.billing_client.query_product_details_async(
287
- product_type=ProductType.SUBS if self.ids.subscribe else ProductType.INAPP,
318
+ product_type=ProductType.SUBS if self.ids.subscribe.active else ProductType.INAPP,
288
319
  products_ids=[product_id],
289
320
  on_product_details_response=self.on_product_details_response,
290
321
  )
@@ -428,6 +459,16 @@ The main class for interacting with Google Play Billing.
428
459
  - `unfetched_product`: Unfetched product object
429
460
  - Returns a dictionary with product ID, type, and status code
430
461
 
462
+ - `query_purchase_async(product_type, on_query_purchases_response)`:
463
+ - Queries purchases asynchronously
464
+ - `product_type`: Type of products (INAPP or SUBS)
465
+ - `on_query_purchases_response`: Callback for purchases response
466
+
467
+ - `get_purchase(purchase)`:
468
+ - Gets formatted purchase details
469
+ - `purchase`: Purchase object
470
+ - Returns a dictionary with purchase details including products, purchase token, purchase state, etc.
471
+
431
472
  #### Purchase Methods
432
473
 
433
474
  - `launch_billing_flow(product_details, offer_token=None)`:
@@ -475,6 +516,41 @@ Result of a product details query.
475
516
  - `getProductDetailsList()`: Gets the list of product details
476
517
  - `getUnfetchedProductList()`: Gets the list of unfetched products
477
518
 
519
+ ### Purchase
520
+
521
+ Represents a purchase made by a user.
522
+
523
+ #### Methods
524
+
525
+ - `getProducts()`: Gets the list of product IDs associated with the purchase
526
+ - `getPurchaseToken()`: Gets the token that uniquely identifies the purchase
527
+
528
+ ### PurchaseState
529
+
530
+ Constants for purchase states:
531
+
532
+ - `PurchaseState.PENDING`: Purchase is pending
533
+ - `PurchaseState.PURCHASED`: Purchase is completed
534
+ - `PurchaseState.UNSPECIFIED`: Purchase state is unspecified
535
+
536
+ ### PendingPurchaseUpdate
537
+
538
+ Represents a pending update to a purchase.
539
+
540
+ #### Methods
541
+
542
+ - `getProducts()`: Gets the list of product IDs associated with the pending purchase update
543
+ - `getPurchaseToken()`: Gets the token that uniquely identifies the pending purchase update
544
+
545
+ ### AccountIdentifiers
546
+
547
+ Contains account identifiers for a purchase.
548
+
549
+ #### Methods
550
+
551
+ - `getObfuscatedAccountId()`: Gets the obfuscated account ID
552
+ - `getProfileId()`: Gets the profile ID
553
+
478
554
  ### ProductType
479
555
 
480
556
  Constants for product types:
@@ -482,6 +558,48 @@ Constants for product types:
482
558
  - `ProductType.INAPP`: One-time purchases
483
559
  - `ProductType.SUBS`: Subscriptions
484
560
 
561
+ ### BillingFlowParamsBuilder
562
+
563
+ Builder for BillingFlowParams.
564
+
565
+ #### Methods
566
+
567
+ - `build()`: Builds the BillingFlowParams object
568
+ - `setIsOfferPersonalized(boolean)`: Sets whether the offer is personalized
569
+ - `setObfuscatedAccountId(String)`: Sets the obfuscated account ID
570
+ - `setObfuscatedProfileId(String)`: Sets the obfuscated profile ID
571
+ - `setProductDetailsParamsList(List)`: Sets the list of product details parameters
572
+ - `setSubscriptionUpdateParams(SubscriptionUpdateParams)`: Sets the subscription update parameters
573
+
574
+ ### SubscriptionUpdateParams
575
+
576
+ Parameters for updating a subscription.
577
+
578
+ #### Methods
579
+
580
+ - `newBuilder()`: Creates a new builder for SubscriptionUpdateParams
581
+ - `build()`: Builds the SubscriptionUpdateParams object
582
+
583
+ ### SubscriptionUpdateParamsBuilder
584
+
585
+ Builder for SubscriptionUpdateParams.
586
+
587
+ #### Methods
588
+
589
+ - `build()`: Builds the SubscriptionUpdateParams object
590
+ - `setOldPurchaseToken(String)`: Sets the token of the old purchase to be replaced
591
+ - `setSubscriptionReplacementMode(int)`: Sets the replacement mode for the subscription
592
+
593
+ ### ReplacementMode
594
+
595
+ Constants for subscription replacement modes:
596
+
597
+ - `ReplacementMode.CHARGE_FULL_PRICE`: Charge the full price for the new subscription
598
+ - `ReplacementMode.CHARGE_PRORATED_PRICE`: Charge a prorated price for the new subscription
599
+ - `ReplacementMode.DEFERRED`: Defer the replacement until the next billing cycle
600
+ - `ReplacementMode.WITHOUT_PRORATION`: Replace without proration
601
+ - `ReplacementMode.WITH_TIME_PRORATION`: Replace with time proration
602
+
485
603
  ### BillingResponseCode
486
604
 
487
605
  Constants for billing response codes:
@@ -1,9 +1,5 @@
1
1
  # SJBillingClient (Google Play Billing SDK for Python)
2
2
 
3
- <!-- GitAds-Verify: 71CCWMQSMVD67LS4WF4N44EXISSL2UTQ -->
4
- ## GitAds Sponsored
5
- [![Sponsored by GitAds](https://gitads.dev/v1/ad-serve?source=simplejnius/sj-android-billingclient@github)](https://gitads.dev/v1/ad-track?source=simplejnius/sj-android-billingclient@github)
6
-
7
3
  ## Overview
8
4
 
9
5
  SJBillingClient is a Python wrapper for the Google Play Billing Library that facilitates in-app purchases and subscriptions in Android applications. It provides a high-level, Pythonic interface to interact with Google Play's billing system, making it easier to implement and manage in-app purchases in Python-based Android apps (like those built with Kivy/Python-for-Android).
@@ -20,14 +16,18 @@ SJBillingClient is a Python wrapper for the Google Play Billing Library that fac
20
16
 
21
17
  - Python 3.9+
22
18
  - pyjnius 1.6.1+
23
- - Android application with Google Play Billing Library (version 8.0.0 recommended)
19
+ - Android application with Google Play Billing Library (version 8.0.0 required)
20
+
21
+ > **Note**: This library is specifically designed for Google Play Billing Library version 8.0.0. Earlier or later versions may not be compatible due to API changes.
24
22
 
25
23
  ## Installation
26
24
 
27
25
  ```shell
28
26
  # Using pip
29
27
  pip install sjbillingclient
28
+ ````
30
29
 
30
+ ```rpmspec
31
31
  # In Buildozer (add to buildozer.spec)
32
32
  requirements = sjbillingclient
33
33
  android.gradle_dependencies = com.android.billingclient:billing:8.0.0
@@ -140,6 +140,37 @@ def on_acknowledge_purchase_response(billing_result):
140
140
  client.acknowledge_purchase(purchase.getPurchaseToken(), on_acknowledge_purchase_response)
141
141
  ```
142
142
 
143
+ ### Querying Purchases
144
+
145
+ ```python
146
+ from sjbillingclient.tools import BillingClient
147
+ from sjbillingclient.jclass.billing import ProductType, BillingResponseCode
148
+ from sjbillingclient.jclass.purchase import PurchaseState
149
+
150
+ def on_query_purchases_response(billing_result, purchases):
151
+ if billing_result.getResponseCode() == BillingResponseCode.OK:
152
+ if purchases and not purchases.isEmpty():
153
+ for i in range(purchases.size()):
154
+ purchase = purchases.get(i)
155
+ # Get formatted purchase details
156
+ purchase_details = client.get_purchase(purchase)
157
+ print(f"Products: {purchase_details['products']}")
158
+ print(f"Purchase token: {purchase_details['purchase_token']}")
159
+ print(f"Purchase state: {purchase_details['purchase_state']}")
160
+
161
+ # Handle the purchase based on its state
162
+ if purchase_details['purchase_state'] == PurchaseState.PURCHASED:
163
+ if not purchase_details['is_acknowledged']:
164
+ # Acknowledge the purchase
165
+ client.acknowledge_purchase(purchase.getPurchaseToken(), on_acknowledge_purchase_response)
166
+
167
+ # Query purchases for a specific product type
168
+ client.query_purchase_async(
169
+ product_type=ProductType.INAPP,
170
+ on_query_purchases_response=on_query_purchases_response
171
+ )
172
+ ```
173
+
143
174
  ### Kivy Integration Example
144
175
 
145
176
  Here's a complete example of integrating SJBillingClient with a Kivy application:
@@ -263,7 +294,7 @@ class HomeScreen(Screen):
263
294
  product_id = self.ids.btn.product_id
264
295
  if billing_result.getResponseCode() == BillingResponseCode.OK:
265
296
  self.billing_client.query_product_details_async(
266
- product_type=ProductType.SUBS if self.ids.subscribe else ProductType.INAPP,
297
+ product_type=ProductType.SUBS if self.ids.subscribe.active else ProductType.INAPP,
267
298
  products_ids=[product_id],
268
299
  on_product_details_response=self.on_product_details_response,
269
300
  )
@@ -407,6 +438,16 @@ The main class for interacting with Google Play Billing.
407
438
  - `unfetched_product`: Unfetched product object
408
439
  - Returns a dictionary with product ID, type, and status code
409
440
 
441
+ - `query_purchase_async(product_type, on_query_purchases_response)`:
442
+ - Queries purchases asynchronously
443
+ - `product_type`: Type of products (INAPP or SUBS)
444
+ - `on_query_purchases_response`: Callback for purchases response
445
+
446
+ - `get_purchase(purchase)`:
447
+ - Gets formatted purchase details
448
+ - `purchase`: Purchase object
449
+ - Returns a dictionary with purchase details including products, purchase token, purchase state, etc.
450
+
410
451
  #### Purchase Methods
411
452
 
412
453
  - `launch_billing_flow(product_details, offer_token=None)`:
@@ -454,6 +495,41 @@ Result of a product details query.
454
495
  - `getProductDetailsList()`: Gets the list of product details
455
496
  - `getUnfetchedProductList()`: Gets the list of unfetched products
456
497
 
498
+ ### Purchase
499
+
500
+ Represents a purchase made by a user.
501
+
502
+ #### Methods
503
+
504
+ - `getProducts()`: Gets the list of product IDs associated with the purchase
505
+ - `getPurchaseToken()`: Gets the token that uniquely identifies the purchase
506
+
507
+ ### PurchaseState
508
+
509
+ Constants for purchase states:
510
+
511
+ - `PurchaseState.PENDING`: Purchase is pending
512
+ - `PurchaseState.PURCHASED`: Purchase is completed
513
+ - `PurchaseState.UNSPECIFIED`: Purchase state is unspecified
514
+
515
+ ### PendingPurchaseUpdate
516
+
517
+ Represents a pending update to a purchase.
518
+
519
+ #### Methods
520
+
521
+ - `getProducts()`: Gets the list of product IDs associated with the pending purchase update
522
+ - `getPurchaseToken()`: Gets the token that uniquely identifies the pending purchase update
523
+
524
+ ### AccountIdentifiers
525
+
526
+ Contains account identifiers for a purchase.
527
+
528
+ #### Methods
529
+
530
+ - `getObfuscatedAccountId()`: Gets the obfuscated account ID
531
+ - `getProfileId()`: Gets the profile ID
532
+
457
533
  ### ProductType
458
534
 
459
535
  Constants for product types:
@@ -461,6 +537,48 @@ Constants for product types:
461
537
  - `ProductType.INAPP`: One-time purchases
462
538
  - `ProductType.SUBS`: Subscriptions
463
539
 
540
+ ### BillingFlowParamsBuilder
541
+
542
+ Builder for BillingFlowParams.
543
+
544
+ #### Methods
545
+
546
+ - `build()`: Builds the BillingFlowParams object
547
+ - `setIsOfferPersonalized(boolean)`: Sets whether the offer is personalized
548
+ - `setObfuscatedAccountId(String)`: Sets the obfuscated account ID
549
+ - `setObfuscatedProfileId(String)`: Sets the obfuscated profile ID
550
+ - `setProductDetailsParamsList(List)`: Sets the list of product details parameters
551
+ - `setSubscriptionUpdateParams(SubscriptionUpdateParams)`: Sets the subscription update parameters
552
+
553
+ ### SubscriptionUpdateParams
554
+
555
+ Parameters for updating a subscription.
556
+
557
+ #### Methods
558
+
559
+ - `newBuilder()`: Creates a new builder for SubscriptionUpdateParams
560
+ - `build()`: Builds the SubscriptionUpdateParams object
561
+
562
+ ### SubscriptionUpdateParamsBuilder
563
+
564
+ Builder for SubscriptionUpdateParams.
565
+
566
+ #### Methods
567
+
568
+ - `build()`: Builds the SubscriptionUpdateParams object
569
+ - `setOldPurchaseToken(String)`: Sets the token of the old purchase to be replaced
570
+ - `setSubscriptionReplacementMode(int)`: Sets the replacement mode for the subscription
571
+
572
+ ### ReplacementMode
573
+
574
+ Constants for subscription replacement modes:
575
+
576
+ - `ReplacementMode.CHARGE_FULL_PRICE`: Charge the full price for the new subscription
577
+ - `ReplacementMode.CHARGE_PRORATED_PRICE`: Charge a prorated price for the new subscription
578
+ - `ReplacementMode.DEFERRED`: Defer the replacement until the next billing cycle
579
+ - `ReplacementMode.WITHOUT_PRORATION`: Replace without proration
580
+ - `ReplacementMode.WITH_TIME_PRORATION`: Replace with time proration
581
+
464
582
  ### BillingResponseCode
465
583
 
466
584
  Constants for billing response codes:
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "sjbillingclient"
3
- version = "0.2.2"
3
+ version = "1.0.0"
4
4
  description = "A Python wrapper for the Google Play Billing Library that facilitates in-app purchases and subscriptions in Android applications"
5
5
  authors = ["Kenechukwu Akubue <kengoon19@gmail.com>"]
6
6
  readme = "README.md"
@@ -0,0 +1,10 @@
1
+ from jnius import JavaClass, MetaJavaClass, JavaMethod
2
+
3
+ __all__ = ("AccountIdentifiers",)
4
+
5
+
6
+ class AccountIdentifiers(JavaClass, metaclass=MetaJavaClass):
7
+ __javaclass__ = "com/android/billingclient/api/AccountIdentifiers"
8
+
9
+ getObfuscatedAccountId = JavaMethod("()Ljava/lang/String;")
10
+ getObfuscatedProfileId = JavaMethod("()Ljava/lang/String;")
@@ -4,5 +4,5 @@ __all__ = ("AcknowledgePurchaseParams",)
4
4
 
5
5
 
6
6
  class AcknowledgePurchaseParams(JavaClass, metaclass=MetaJavaClass):
7
- __javaclass__ = f"com/android/billingclient/api/AcknowledgePurchaseParams"
7
+ __javaclass__ = "com/android/billingclient/api/AcknowledgePurchaseParams"
8
8
  newBuilder = JavaStaticMethod("()Lcom/android/billingclient/api/AcknowledgePurchaseParams$Builder;")
@@ -1,7 +1,8 @@
1
1
  from jnius import JavaClass, MetaJavaClass, JavaStaticMethod, JavaStaticField, JavaMethod, JavaMultipleMethod
2
2
 
3
- __all__ = ("BillingClient", "BillingFlowParams", "ProductType", "GetBillingConfigParams",
4
- "ProductDetailsParams", "BillingResponseCode")
3
+ __all__ = ("BillingClient", "BillingFlowParams", "BillingFlowParamsBuilder", "ProductType", "GetBillingConfigParams",
4
+ "ProductDetailsParams", "BillingResponseCode", "SubscriptionUpdateParams", "SubscriptionUpdateParamsBuilder",
5
+ "ReplacementMode")
5
6
 
6
7
 
7
8
  class BillingClient(JavaClass, metaclass=MetaJavaClass):
@@ -54,27 +55,28 @@ class BillingClient(JavaClass, metaclass=MetaJavaClass):
54
55
  "(Lcom/android/billingclient/api/QueryProductDetailsParams;"
55
56
  "Lcom/android/billingclient/api/ProductDetailsResponseListener;)V"
56
57
  )
57
- queryPurchaseHistoryAsync = JavaMultipleMethod([
58
- ("(Lcom/android/billingclient/api/QueryPurchaseHistoryParams;"
59
- "Lcom/android/billingclient/api/PurchaseHistoryResponseListener;)V",
60
- False, False),
61
- ("(Ljava/lang/String;Lcom/android/billingclient/api/PurchaseHistoryResponseListener;)V,",
62
- False, False),
63
- ])
64
- queryPurchasesAsync = JavaMultipleMethod([
65
- ("(Lcom/android/billingclient/api/QueryPurchasesParams;"
66
- "Lcom/android/billingclient/api/PurchasesResponseListener;)V", False, False),
67
- ("(Ljava/lang/String;Lcom/android/billingclient/api/PurchasesResponseListener;)V",
68
- False, False),
69
- ])
70
- querySkuDetailsAsync = JavaMethod(
71
- "(Lcom/android/billingclient/api/SkuDetailsParams;"
72
- "Lcom/android/billingclient/api/SkuDetailsResponseListener;)V"
58
+ queryPurchasesAsync = JavaMethod(
59
+ "(Lcom/android/billingclient/api/QueryPurchasesParams;"
60
+ "Lcom/android/billingclient/api/PurchasesResponseListener;)V"
73
61
  )
74
62
  startConnection = JavaMethod("(Lcom/android/billingclient/api/BillingClientStateListener;)V")
75
63
  isReady = JavaMethod("()Z")
76
64
 
77
65
 
66
+ class BillingClientBuilder(JavaClass, metaclass=MetaJavaClass):
67
+ __javaclass__ = "com/android/billingclient/api/BillingClient$Builder"
68
+ build = JavaMethod("()Lcom/android/billingclient/api/BillingClient;")
69
+ enableAlternativeBillingOnly = JavaMethod("()Lcom/android/billingclient/api/BillingClient$Builder;")
70
+ enableAutoServiceReconnection = JavaMethod("()Lcom/android/billingclient/api/BillingClient$Builder;")
71
+ enableExternalOffer = JavaMethod("()Lcom/android/billingclient/api/BillingClient$Builder;")
72
+ enablePendingPurchases = JavaMethod("(Lcom/android/billingclient/api/PendingPurchasesParams;)"
73
+ "Lcom/android/billingclient/api/BillingClient$Builder;")
74
+ enableUserChoiceBilling = JavaMethod("(Lcom/android/billingclient/api/UserChoiceBillingListener;)"
75
+ "Lcom/android/billingclient/api/BillingClient$Builder;")
76
+ setListener = JavaMethod("(Lcom/android/billingclient/api/PurchasesUpdatedListener;)"
77
+ "Lcom/android/billingclient/api/BillingClient$Builder;")
78
+
79
+
78
80
  class ProductType(JavaClass, metaclass=MetaJavaClass):
79
81
  __javaclass__ = "com/android/billingclient/api/BillingClient$ProductType"
80
82
  INAPP = JavaStaticField("Ljava/lang/String;")
@@ -102,6 +104,19 @@ class BillingFlowParams(JavaClass, metaclass=MetaJavaClass):
102
104
  newBuilder = JavaStaticMethod("()Lcom/android/billingclient/api/BillingFlowParams$Builder;")
103
105
 
104
106
 
107
+ class BillingFlowParamsBuilder(JavaClass, metaclass=MetaJavaClass):
108
+ __javaclass__ = "com/android/billingclient/api/BillingFlowParams$Builder"
109
+ build = JavaMethod("()Lcom/android/billingclient/api/BillingFlowParams;")
110
+ setIsOfferPersonalized = JavaMethod("(Z)Lcom/android/billingclient/api/BillingFlowParams$Builder;")
111
+ setObfuscatedAccountId = JavaMethod("(Ljava/lang/String;)Lcom/android/billingclient/api/BillingFlowParams$Builder;")
112
+ setObfuscatedProfileId = JavaMethod("(Ljava/lang/String;)Lcom/android/billingclient/api/BillingFlowParams$Builder;")
113
+ setProductDetailsParamsList = JavaMethod("(Ljava/util/List;)Lcom/android/billingclient/api"
114
+ "/BillingFlowParams$Builder;")
115
+ setSubscriptionUpdateParams = JavaMethod("(Lcom/android/billingclient/api"
116
+ "/BillingFlowParams$SubscriptionUpdateParams;)Lcom/android/billingclient"
117
+ "/api/BillingFlowParams$Builder;")
118
+
119
+
105
120
  class GetBillingConfigParams(JavaClass, metaclass=MetaJavaClass):
106
121
  __javaclass__ = "com/android/billingclient/api/GetBillingConfigParams"
107
122
  newBuilder = JavaStaticMethod("()Lcom/android/billingclient/api/GetBillingConfigParams$Builder;")
@@ -110,3 +125,28 @@ class GetBillingConfigParams(JavaClass, metaclass=MetaJavaClass):
110
125
  class ProductDetailsParams(JavaClass, metaclass=MetaJavaClass):
111
126
  __javaclass__ = "com/android/billingclient/api/BillingFlowParams$ProductDetailsParams"
112
127
  newBuilder = JavaStaticMethod("()Lcom/android/billingclient/api/BillingFlowParams$ProductDetailsParams$Builder;")
128
+
129
+
130
+ class SubscriptionUpdateParams(JavaClass, metaclass=MetaJavaClass):
131
+ __javaclass__ = "com/android/billingclient/api/BillingFlowParams$SubscriptionUpdateParams"
132
+ newBuilder = JavaStaticMethod("()Lcom/android/billingclient/api/BillingFlowParams"
133
+ "$SubscriptionUpdateParams$Builder;")
134
+
135
+
136
+ class SubscriptionUpdateParamsBuilder(JavaClass, metaclass=MetaJavaClass):
137
+ __javaclass__ = "com/android/billingclient/api/BillingFlowParams$SubscriptionUpdateParams$Builder"
138
+ build = JavaMethod("()Lcom/android/billingclient/api/BillingFlowParams$SubscriptionUpdateParams;")
139
+ setOldPurchaseToken = JavaMethod("(Ljava/lang/String;)Lcom/android/billingclient/api"
140
+ "/BillingFlowParams$SubscriptionUpdateParams$Builder;")
141
+ setSubscriptionReplacementMode = JavaMethod("(I)Lcom/android/billingclient/api"
142
+ "/BillingFlowParams$SubscriptionUpdateParams$Builder;")
143
+
144
+
145
+ class ReplacementMode(JavaClass, metaclass=MetaJavaClass):
146
+ __javaclass__ = "com/android/billingclient/api/BillingFlowParams$SubscriptionUpdateParams$ReplacementMode"
147
+ CHARGE_FULL_PRICE = JavaStaticField("I")
148
+ CHARGE_PRORATED_PRICE = JavaStaticField("I")
149
+ DEFERRED = JavaStaticField("I")
150
+ UNKNOWN_REPLACEMENT_MODE = JavaStaticField("I")
151
+ WITHOUT_PRORATION = JavaStaticField("I")
152
+ WITH_TIME_PRORATION = JavaStaticField("I")
@@ -4,5 +4,5 @@ __all__ = ("ConsumeParams",)
4
4
 
5
5
 
6
6
  class ConsumeParams(JavaClass, metaclass=MetaJavaClass):
7
- __javaclass__ = f"com/android/billingclient/api/ConsumeParams"
7
+ __javaclass__ = "com/android/billingclient/api/ConsumeParams"
8
8
  newBuilder = JavaStaticMethod("()Lcom/android/billingclient/api/ConsumeParams$Builder;")
@@ -0,0 +1,54 @@
1
+ from jnius import JavaClass, MetaJavaClass, JavaStaticMethod, JavaMethod, JavaStaticField
2
+
3
+ __all__ = ("PendingPurchasesParams", "PendingPurchasesParamsBuilder", "Purchase", "PurchaseState", "PendingPurchaseUpdate")
4
+
5
+
6
+ class PendingPurchasesParams(JavaClass, metaclass=MetaJavaClass):
7
+ __javaclass__ = "com/android/billingclient/api/PendingPurchasesParams"
8
+ newBuilder = JavaStaticMethod("()Lcom/android/billingclient/api/PendingPurchasesParams$Builder;")
9
+
10
+
11
+ class PendingPurchasesParamsBuilder(JavaClass, metaclass=MetaJavaClass):
12
+ __javaclass__ = "com/android/billingclient/api/PendingPurchasesParams$Builder"
13
+
14
+ build = JavaMethod("()Lcom/android/billingclient/api/PendingPurchasesParams;")
15
+ enableOneTimeProducts = JavaMethod("()Lcom/android/billingclient/api/PendingPurchasesParams$Builder;")
16
+ enablePrepaidPlans = JavaMethod("()Lcom/android/billingclient/api/PendingPurchasesParams$Builder;")
17
+
18
+
19
+ class Purchase(JavaClass, metaclass=MetaJavaClass):
20
+ __javaclass__ = "com/android/billingclient/api/Purchase"
21
+
22
+ getAccountIdentifiers = JavaMethod("()Lcom/android/billingclient/api/AccountIdentifiers;")
23
+ getDeveloperPayload = JavaMethod("()Ljava/lang/String;")
24
+ getOrderId = JavaMethod("()Ljava/lang/String;")
25
+ getOriginalJson = JavaMethod("()Ljava/lang/String;")
26
+ getPackageName = JavaMethod("()Ljava/lang/String;")
27
+ getPendingPurchaseUpdate = JavaMethod("()Lcom/android/billingclient/api/Purchase$PendingPurchaseUpdate;")
28
+ getProducts = JavaMethod("()Ljava/util/List;")
29
+ getPurchaseState = JavaMethod("()I")
30
+ getPurchaseTime = JavaMethod("()J")
31
+ getPurchaseToken = JavaMethod("()Ljava/lang/String;")
32
+ getQuantity = JavaMethod("()I")
33
+ getSignature = JavaMethod("()Ljava/lang/String;")
34
+ hashCode = JavaMethod("()I")
35
+ isAcknowledged = JavaMethod("()Z")
36
+ isAutoRenewing = JavaMethod("()Z")
37
+ toString = JavaMethod("()Ljava/lang/String;")
38
+
39
+
40
+ class PurchaseState(JavaClass, metaclass=MetaJavaClass):
41
+ __javaclass__ = "com/android/billingclient/api/Purchase$PurchaseState"
42
+
43
+ # Purchase state constants
44
+ PENDING = JavaStaticField("I")
45
+ PURCHASED = JavaStaticField("I")
46
+ UNSPECIFIED = JavaStaticField("I")
47
+
48
+
49
+ class PendingPurchaseUpdate(JavaClass, metaclass=MetaJavaClass):
50
+ __javaclass__ = "com/android/billingclient/api/Purchase$PendingPurchaseUpdate"
51
+
52
+ getProducts = JavaMethod("()Ljava/util/List;")
53
+ getPurchaseToken = JavaMethod("()Ljava/lang/String;")
54
+
@@ -0,0 +1,36 @@
1
+ from jnius import JavaClass, MetaJavaClass, JavaStaticMethod, JavaMethod
2
+
3
+ __all__ = ("QueryProductDetailsParams", "QueryProductDetailsParamsProduct", "QueryProductDetailsParamsBuilder", "QueryProductDetailsParamsProductBuilder")
4
+
5
+
6
+ class QueryProductDetailsParams(JavaClass, metaclass=MetaJavaClass):
7
+ __javaclass__ = "com/android/billingclient/api/QueryProductDetailsParams"
8
+ newBuilder = JavaStaticMethod("()Lcom/android/billingclient/api/QueryProductDetailsParams$Builder;")
9
+
10
+
11
+ class QueryProductDetailsParamsProduct(JavaClass, metaclass=MetaJavaClass):
12
+ __javaclass__ = "com/android/billingclient/api/QueryProductDetailsParams$Product"
13
+ newBuilder = JavaStaticMethod("()Lcom/android/billingclient/api/QueryProductDetailsParams$Product$Builder;")
14
+
15
+
16
+ class QueryProductDetailsParamsProductBuilder(JavaClass, metaclass=MetaJavaClass):
17
+ __javaclass__ = "com/android/billingclient/api/QueryProductDetailsParams$Product$Builder"
18
+ build = JavaMethod("()Lcom/android/billingclient/api/QueryProductDetailsParams$Product;")
19
+ setProductId = JavaMethod("(Ljava/lang/String;)"
20
+ "Lcom/android/billingclient/api/QueryProductDetailsParams$Product$Builder;")
21
+ setProductType = JavaMethod("(Ljava/lang/String;)"
22
+ "Lcom/android/billingclient/api/QueryProductDetailsParams$Product$Builder;")
23
+
24
+
25
+ class QueryProductDetailsParamsBuilder(JavaClass, metaclass=MetaJavaClass):
26
+ __javaclass__ = "com/android/billingclient/api/QueryProductDetailsParams$Builder"
27
+ build = JavaMethod("()Lcom/android/billingclient/api/QueryProductDetailsParams;")
28
+ setProductList = JavaMethod("(Ljava/util/List;)Lcom/android/billingclient/api/QueryProductDetailsParams$Builder;")
29
+
30
+
31
+ class QueryProductDetailsResult(JavaClass, metaclass=MetaJavaClass):
32
+ __javaclass__ = "com/android/billingclient/api/QueryProductDetailsResult"
33
+ create = JavaStaticMethod("(Ljava/util/List;Ljava/util/List;)"
34
+ "Lcom/android/billingclient/api/QueryProductDetailsResult;")
35
+ getProductDetailsList = JavaMethod("()Ljava/util/List;")
36
+ getUnfetchedProductList = JavaMethod("()Ljava/util/List;")
@@ -0,0 +1,14 @@
1
+ from jnius import JavaClass, MetaJavaClass, JavaStaticMethod, JavaMethod
2
+
3
+ __all__ = ("QueryPurchasesParams", "QueryPurchasesParamsBuilder")
4
+
5
+
6
+ class QueryPurchasesParams(JavaClass, metaclass=MetaJavaClass):
7
+ __javaclass__ = "com/android/billingclient/api/QueryPurchasesParams"
8
+ newBuilder = JavaStaticMethod("()Lcom/android/billingclient/api/QueryPurchasesParams$Builder;")
9
+
10
+
11
+ class QueryPurchasesParamsBuilder(JavaClass, metaclass=MetaJavaClass):
12
+ __javaclass__ = "com/android/billingclient/api/QueryPurchasesParams$Builder"
13
+ build = JavaMethod("()Lcom/android/billingclient/api/QueryPurchasesParams;")
14
+ setProductType = JavaMethod("(Ljava/lang/String;)Lcom/android/billingclient/api/QueryPurchasesParams$Builder;")
@@ -1,4 +1,4 @@
1
- __all__ = ("PurchasesUpdatedListener", )
1
+ __all__ = ("PurchasesUpdatedListener", "PurchasesResponseListener")
2
2
 
3
3
  from jnius import PythonJavaClass, java_method
4
4
  from sjbillingclient import is_jnull
@@ -14,3 +14,15 @@ class PurchasesUpdatedListener(PythonJavaClass):
14
14
  @java_method("(Lcom/android/billingclient/api/BillingResult;Ljava/util/List;)V")
15
15
  def onPurchasesUpdated(self, billing_result, purchases):
16
16
  self.callback(billing_result, is_jnull(purchases), purchases)
17
+
18
+
19
+ class PurchasesResponseListener(PythonJavaClass):
20
+ __javainterfaces__ = ["com/android/billingclient/api/PurchasesResponseListener"]
21
+ __javacontext__ = "app"
22
+
23
+ def __init__(self, callback):
24
+ self.callback = callback
25
+
26
+ @java_method("(Lcom/android/billingclient/api/BillingResult;Ljava/util/List;)V")
27
+ def onQueryPurchasesResponse(self, billing_result, purchases):
28
+ self.callback(billing_result, purchases)
@@ -44,11 +44,12 @@ from android.activity import _activity as activity # noqa
44
44
  from sjbillingclient.jclass.consume import ConsumeParams
45
45
  from sjbillingclient.jclass.purchase import PendingPurchasesParams
46
46
  from sjbillingclient.jclass.queryproduct import QueryProductDetailsParams, QueryProductDetailsParamsProduct
47
+ from sjbillingclient.jclass.querypurchases import QueryPurchasesParams
47
48
  from sjbillingclient.jinterface.acknowledge import AcknowledgePurchaseResponseListener
48
49
  from sjbillingclient.jinterface.billing import BillingClientStateListener
49
50
  from sjbillingclient.jinterface.consume import ConsumeResponseListener
50
51
  from sjbillingclient.jinterface.product import ProductDetailsResponseListener
51
- from sjbillingclient.jinterface.purchases import PurchasesUpdatedListener
52
+ from sjbillingclient.jinterface.purchases import PurchasesUpdatedListener, PurchasesResponseListener
52
53
 
53
54
  ERROR_NO_BASE_PLAN = "You don't have a base plan"
54
55
  ERROR_NO_BASE_PLAN_ID = "You don't have a base plan id"
@@ -76,13 +77,16 @@ class BillingClient:
76
77
  :type __consume_response_listener: ConsumeResponseListener | None
77
78
  :ivar __acknowledge_purchase_response_listener: Listener handling responses for acknowledging purchases.
78
79
  :type __acknowledge_purchase_response_listener: AcknowledgePurchaseResponseListener | None
80
+ :type __purchases_response_listener: PurchasesResponseListener | None handling responses for purchase queries.
79
81
  """
80
82
 
81
83
  def __init__(
82
84
  self,
83
85
  on_purchases_updated,
86
+ enable_auto_service_reconnection: bool = True,
84
87
  enable_one_time_products: bool = True,
85
- enable_prepaid_plans: bool = False
88
+ enable_prepaid_plans: bool = False,
89
+ enable_external_offer: bool = False,
86
90
  ) -> None:
87
91
  """
88
92
  Initializes an instance of the class with the given purchase update callback.
@@ -96,6 +100,7 @@ class BillingClient:
96
100
  self.__product_details_response_listener = None
97
101
  self.__consume_response_listener = None
98
102
  self.__acknowledge_purchase_response_listener = None
103
+ self.__purchases_response_listener = None
99
104
 
100
105
  self.__purchase_update_listener = PurchasesUpdatedListener(on_purchases_updated)
101
106
  pending_purchase_params = PendingPurchasesParams.newBuilder()
@@ -103,14 +108,18 @@ class BillingClient:
103
108
  pending_purchase_params.enableOneTimeProducts()
104
109
  if enable_prepaid_plans:
105
110
  pending_purchase_params.enablePrepaidPlans()
106
- self.__billing_client = (
107
- SJBillingClient.newBuilder(activity.context)
108
- .setListener(self.__purchase_update_listener)
109
- .enablePendingPurchases(pending_purchase_params.build())
111
+
112
+ billing_client = SJBillingClient.newBuilder(activity.context)
113
+ if enable_external_offer:
114
+ billing_client.enableExternalOffer()
115
+ if enable_auto_service_reconnection:
116
+ billing_client.enableAutoServiceReconnection()
117
+ self.__billing_client = billing_client \
118
+ .setListener(self.__purchase_update_listener) \
119
+ .enablePendingPurchases(pending_purchase_params.build()) \
110
120
  .build()
111
- )
112
121
 
113
- def start_connection(self, on_billing_setup_finished, on_billing_service_disconnected) -> None:
122
+ def start_connection(self, on_billing_setup_finished, on_billing_service_disconnected=lambda: None) -> None:
114
123
  """
115
124
  Starts a connection with the billing client and initializes the billing
116
125
  client state listener. This method sets up a listener to handle billing
@@ -140,6 +149,26 @@ class BillingClient:
140
149
  """
141
150
  self.__billing_client.endConnection()
142
151
 
152
+ def query_purchase_async(self, product_type: str, on_query_purchases_response) -> None:
153
+ """
154
+ Queries purchases asynchronously for a given product type.
155
+
156
+ This function utilizes the provided purchases response callback to handle the
157
+ resulting response from the query.
158
+
159
+ :param product_type: The type of the products to query purchases for (e.g., "inapp" or "subs").
160
+ :param on_query_purchases_response: A callback function that is triggered when the
161
+ purchases query is complete.
162
+ :return: None
163
+ """
164
+
165
+ params = (QueryPurchasesParams.newBuilder()
166
+ .setProductType(product_type)
167
+ .build())
168
+
169
+ self.__purchases_response_listener = PurchasesResponseListener(on_query_purchases_response)
170
+ self.__billing_client.queryPurchasesAsync(params, self.__purchases_response_listener)
171
+
143
172
  def query_product_details_async(self, product_type: str, products_ids: List[str],
144
173
  on_product_details_response) -> None:
145
174
  """
@@ -188,6 +217,46 @@ class BillingClient:
188
217
  .setProductType(product_type)
189
218
  .build())
190
219
 
220
+ @staticmethod
221
+ def get_purchase(purchase) -> Dict:
222
+ """
223
+ Retrieves detailed information from a purchase object.
224
+
225
+ This function extracts important details from a purchase object, such as
226
+ product IDs, purchase token, purchase state, and other relevant information.
227
+ The extracted details are then returned as a dictionary.
228
+
229
+ :param purchase: The purchase object to extract information from.
230
+ :type purchase: Purchase
231
+ :return: A dictionary containing detailed information about the purchase.
232
+ :rtype: Dict
233
+ """
234
+ account_identifiers = purchase.getAccountIdentifiers()
235
+ pending_purchase_update = purchase.getPendingPurchaseUpdate()
236
+
237
+ return {
238
+ "products": list(purchase.getProducts()),
239
+ "purchase_token": purchase.getPurchaseToken(),
240
+ "purchase_state": purchase.getPurchaseState(),
241
+ "purchase_time": purchase.getPurchaseTime(),
242
+ "order_id": purchase.getOrderId(),
243
+ "quantity": purchase.getQuantity(),
244
+ "is_acknowledged": purchase.isAcknowledged(),
245
+ "is_auto_renewing": purchase.isAutoRenewing(),
246
+ "original_json": purchase.getOriginalJson(),
247
+ "signature": purchase.getSignature(),
248
+ "package_name": purchase.getPackageName(),
249
+ "developer_payload": purchase.getDeveloperPayload(),
250
+ "account_identifiers": {
251
+ "obfuscated_account_id": account_identifiers.getObfuscatedAccountId(),
252
+ "obfuscated_profile_id": account_identifiers.getObfuscatedProfileId(),
253
+ },
254
+ "pending_purchase_update": {
255
+ "products": list(pending_purchase_update.getProducts() or []),
256
+ "purchase_token": pending_purchase_update.getPurchaseToken(),
257
+ } if pending_purchase_update else None
258
+ }
259
+
191
260
  @staticmethod
192
261
  def get_unfetched_product(unfetched_product) -> Dict:
193
262
  """
@@ -325,7 +394,6 @@ class BillingClient:
325
394
  self._create_product_params(product_detail, offer_token)
326
395
  for product_detail in product_details
327
396
  ]
328
- print(product_params_list)
329
397
 
330
398
  billing_flow_params = (BillingFlowParams.newBuilder()
331
399
  .setProductDetailsParamsList(JavaList.of(*product_params_list))
@@ -1,16 +0,0 @@
1
- from jnius import JavaClass, MetaJavaClass, JavaStaticMethod, JavaMethod
2
-
3
- __all__ = ("PendingPurchasesParams", "PendingPurchasesParamsBuilder")
4
-
5
-
6
- class PendingPurchasesParams(JavaClass, metaclass=MetaJavaClass):
7
- __javaclass__ = f"com/android/billingclient/api/PendingPurchasesParams"
8
- newBuilder = JavaStaticMethod("()Lcom/android/billingclient/api/PendingPurchasesParams$Builder;")
9
-
10
-
11
- class PendingPurchasesParamsBuilder(JavaClass, metaclass=MetaJavaClass):
12
- __javaclass__ = f"com/android/billingclient/api/PendingPurchasesParams$Builder"
13
-
14
- build = JavaMethod("()Lcom/android/billingclient/api/PendingPurchasesParams;")
15
- enableOneTimeProducts = JavaMethod("()Lcom/android/billingclient/api/PendingPurchasesParams$Builder;")
16
- enablePrepaidPlans = JavaMethod("()Lcom/android/billingclient/api/PendingPurchasesParams$Builder;")
@@ -1,21 +0,0 @@
1
- from jnius import JavaClass, MetaJavaClass, JavaStaticMethod, JavaMethod
2
-
3
- __all__ = ("QueryProductDetailsParams", "QueryProductDetailsParamsProduct")
4
-
5
-
6
- class QueryProductDetailsParams(JavaClass, metaclass=MetaJavaClass):
7
- __javaclass__ = f"com/android/billingclient/api/QueryProductDetailsParams"
8
- newBuilder = JavaStaticMethod("()Lcom/android/billingclient/api/QueryProductDetailsParams$Builder;")
9
-
10
-
11
- class QueryProductDetailsParamsProduct(JavaClass, metaclass=MetaJavaClass):
12
- __javaclass__ = f"com/android/billingclient/api/QueryProductDetailsParams$Product"
13
- newBuilder = JavaStaticMethod("()Lcom/android/billingclient/api/QueryProductDetailsParams$Product$Builder;")
14
-
15
-
16
- class QueryProductDetailsResult(JavaClass, metaclass=MetaJavaClass):
17
- __javaclass__ = f"com/android/billingclient/api/QueryProductDetailsResult"
18
- create = JavaStaticMethod("(Ljava/util/List;Ljava/util/List;)"
19
- "Lcom/android/billingclient/api/QueryProductDetailsResult;")
20
- getProductDetailsList = JavaMethod("()Ljava/util/List;")
21
- getUnfetchedProductList = JavaMethod("()Ljava/util/List;")