sjbillingclient 0.1.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.
@@ -0,0 +1,13 @@
1
+ Metadata-Version: 2.1
2
+ Name: sjbillingclient
3
+ Version: 0.1.0
4
+ Summary:
5
+ Author: Kenechukwu Akubue
6
+ Author-email: kengoon19@gmail.com
7
+ Requires-Python: >=3.13,<4.0
8
+ Classifier: Programming Language :: Python :: 3
9
+ Requires-Dist: pyjnius (>=1.6.1,<2.0.0)
10
+ Description-Content-Type: text/markdown
11
+
12
+ # SJBillingClient (Google Play Billing SDK for Python)
13
+
@@ -0,0 +1 @@
1
+ # SJBillingClient (Google Play Billing SDK for Python)
@@ -0,0 +1,15 @@
1
+ [tool.poetry]
2
+ name = "sjbillingclient"
3
+ version = "0.1.0"
4
+ description = ""
5
+ authors = ["Kenechukwu Akubue <kengoon19@gmail.com>"]
6
+ readme = "README.md"
7
+
8
+ [tool.poetry.dependencies]
9
+ python = "^3.13"
10
+ pyjnius = "^1.6.1"
11
+
12
+
13
+ [build-system]
14
+ requires = ["poetry-core"]
15
+ build-backend = "poetry.core.masonry.api"
@@ -0,0 +1,6 @@
1
+ from jnius import autoclass
2
+
3
+
4
+ def is_jnull(obj):
5
+ Objects = autoclass("java.util.Objects")
6
+ return Objects.isNull(obj)
@@ -0,0 +1,8 @@
1
+ from jnius import JavaClass, MetaJavaClass, JavaStaticMethod
2
+
3
+ __all__ = ("AcknowledgePurchaseParams",)
4
+
5
+
6
+ class AcknowledgePurchaseParams(JavaClass, metaclass=MetaJavaClass):
7
+ __javaclass__ = f"com/android/billingclient/api/AcknowledgePurchaseParams"
8
+ newBuilder = JavaStaticMethod("()Lcom/android/billingclient/api/AcknowledgePurchaseParams$Builder;")
@@ -0,0 +1,112 @@
1
+ from jnius import JavaClass, MetaJavaClass, JavaStaticMethod, JavaStaticField, JavaMethod, JavaMultipleMethod
2
+
3
+ __all__ = ("BillingClient", "BillingFlowParams", "ProductType", "GetBillingConfigParams",
4
+ "ProductDetailsParams")
5
+
6
+
7
+ class BillingClient(JavaClass, metaclass=MetaJavaClass):
8
+ __javaclass__ = "com/android/billingclient/api/BillingClient"
9
+ newBuilder = JavaStaticMethod("(Landroid/content/Context;)Lcom/android/billingclient/api/BillingClient$Builder;")
10
+ isFeatureSupported = JavaMethod("(Ljava/lang/String;)Lcom/android/billingclient/api/BillingResult;")
11
+ launchBillingFlow = JavaMethod(
12
+ "(Landroid/app/Activity;Lcom/android/billingclient/api/BillingFlowParams;)"
13
+ "Lcom/android/billingclient/api/BillingResult;"
14
+ )
15
+ showAlternativeBillingOnlyInformationDialog = JavaMethod(
16
+ "(Landroid/app/Activity;Lcom/android/billingclient/api/AlternativeBillingOnlyInformationDialogListener;)"
17
+ "Lcom/android/billingclient/api/BillingResult;"
18
+ )
19
+ showExternalOfferInformationDialog = JavaMethod(
20
+ "(Landroid/app/Activity;Lcom/android/billingclient/api/ExternalOfferInformationDialogListener;)"
21
+ "Lcom/android/billingclient/api/BillingResult;"
22
+ )
23
+ showInAppMessages = JavaMethod(
24
+ "(Landroid/app/Activity;Lcom/android/billingclient/api/InAppMessageParams;"
25
+ "Lcom/android/billingclient/api/InAppMessageResponseListener;)"
26
+ "Lcom/android/billingclient/api/BillingResult;"
27
+ )
28
+ acknowledgePurchase = JavaMethod(
29
+ "(Lcom/android/billingclient/api/AcknowledgePurchaseParams;"
30
+ "Lcom/android/billingclient/api/AcknowledgePurchaseResponseListener;)V"
31
+ )
32
+ consumeAsync = JavaMethod(
33
+ "(Lcom/android/billingclient/api/ConsumeParams;"
34
+ "Lcom/android/billingclient/api/ConsumeResponseListener;)V"
35
+ )
36
+ createAlternativeBillingOnlyReportingDetailsAsync = JavaMethod(
37
+ "(Lcom/android/billingclient/api/AlternativeBillingOnlyReportingDetailsListener;)V"
38
+ )
39
+ createExternalOfferReportingDetailsAsync = JavaMethod(
40
+ "(Lcom/android/billingclient/api/ExternalOfferReportingDetailsListener;)V"
41
+ )
42
+ endConnection = JavaMethod("()V")
43
+ getBillingConfigAsync = JavaMethod(
44
+ "(Lcom/android/billingclient/api/GetBillingConfigParams;"
45
+ "Lcom/android/billingclient/api/BillingConfigResponseListener;)V"
46
+ )
47
+ isAlternativeBillingOnlyAvailableAsync = JavaMethod(
48
+ "(Lcom/android/billingclient/api/AlternativeBillingOnlyAvailabilityListener;)V"
49
+ )
50
+ isExternalOfferAvailableAsync = JavaMethod(
51
+ "(Lcom/android/billingclient/api/ExternalOfferAvailabilityListener;)V"
52
+ )
53
+ queryProductDetailsAsync = JavaMethod(
54
+ "(Lcom/android/billingclient/api/QueryProductDetailsParams;"
55
+ "Lcom/android/billingclient/api/ProductDetailsResponseListener;)V"
56
+ )
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"
73
+ )
74
+ startConnection = JavaMethod("(Lcom/android/billingclient/api/BillingClientStateListener;)V")
75
+ isReady = JavaMethod("()Z")
76
+
77
+
78
+ class ProductType(JavaClass, metaclass=MetaJavaClass):
79
+ __javaclass__ = "com/android/billingclient/api/BillingClient$ProductType"
80
+ INAPP = JavaStaticField("Ljava/lang/String;")
81
+ SUBS = JavaStaticField("Ljava/lang/String;")
82
+
83
+
84
+ class BillingResponseCode(JavaClass, metaclass=MetaJavaClass):
85
+ __javaclass__ = "com/android/billingclient/api/BillingClient$BillingResponseCode"
86
+ BILLING_UNAVAILABLE = JavaStaticField("I")
87
+ DEVELOPER_ERROR = JavaStaticField("I")
88
+ ERROR = JavaStaticField("I")
89
+ FEATURE_NOT_SUPPORTED = JavaStaticField("I")
90
+ ITEM_ALREADY_OWNED = JavaStaticField("I")
91
+ ITEM_NOT_OWNED = JavaStaticField("I")
92
+ ITEM_UNAVAILABLE = JavaStaticField("I")
93
+ NETWORK_ERROR = JavaStaticField("I")
94
+ OK = JavaStaticField("I")
95
+ SERVICE_DISCONNECTED = JavaStaticField("I")
96
+ SERVICE_UNAVAILABLE = JavaStaticField("I")
97
+ USER_CANCELED = JavaStaticField("I")
98
+
99
+
100
+ class BillingFlowParams(JavaClass, metaclass=MetaJavaClass):
101
+ __javaclass__ = "com/android/billingclient/api/BillingFlowParams"
102
+ newBuilder = JavaStaticMethod("()Lcom/android/billingclient/api/BillingFlowParams$Builder;")
103
+
104
+
105
+ class GetBillingConfigParams(JavaClass, metaclass=MetaJavaClass):
106
+ __javaclass__ = "com/android/billingclient/api/GetBillingConfigParams"
107
+ newBuilder = JavaStaticMethod("()Lcom/android/billingclient/api/GetBillingConfigParams$Builder;")
108
+
109
+
110
+ class ProductDetailsParams(JavaClass, metaclass=MetaJavaClass):
111
+ __javaclass__ = "com/android/billingclient/api/BillingFlowParams$ProductDetailsParams"
112
+ newBuilder = JavaStaticMethod("()Lcom/android/billingclient/api/BillingFlowParams$ProductDetailsParams$Builder;")
@@ -0,0 +1,8 @@
1
+ from jnius import JavaClass, MetaJavaClass, JavaStaticMethod
2
+
3
+ __all__ = ("ConsumeParams",)
4
+
5
+
6
+ class ConsumeParams(JavaClass, metaclass=MetaJavaClass):
7
+ __javaclass__ = f"com/android/billingclient/api/ConsumeParams"
8
+ newBuilder = JavaStaticMethod("()Lcom/android/billingclient/api/ConsumeParams$Builder;")
@@ -0,0 +1,8 @@
1
+ from jnius import JavaClass, MetaJavaClass, JavaStaticMethod
2
+
3
+ __all__ = ("PendingPurchasesParams",)
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;")
@@ -0,0 +1,13 @@
1
+ from jnius import JavaClass, MetaJavaClass, JavaStaticMethod
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;")
@@ -0,0 +1,15 @@
1
+ from jnius import PythonJavaClass, java_method
2
+
3
+ __all__ = ("AcknowledgePurchaseResponseListener", )
4
+
5
+
6
+ class AcknowledgePurchaseResponseListener(PythonJavaClass):
7
+ __javainterfaces__ = ["com/android/billingclient/api/AcknowledgePurchaseResponseListener"]
8
+ __javacontext__ = "app"
9
+
10
+ def __init__(self, callback):
11
+ self.callback = callback
12
+
13
+ @java_method("(Lcom/android/billingclient/api/BillingResult;)V")
14
+ def onAcknowledgePurchaseResponse(self, billing_result):
15
+ self.callback(billing_result)
@@ -0,0 +1,32 @@
1
+ from jnius import PythonJavaClass, java_method
2
+
3
+ __all__ = ("BillingClientStateListener", )
4
+
5
+
6
+ class BillingClientStateListener(PythonJavaClass):
7
+ __javainterfaces__ = ["com/android/billingclient/api/BillingClientStateListener"]
8
+ __javacontext__ = "app"
9
+
10
+ def __init__(self, on_billing_setup_finished, on_billing_service_disconnected):
11
+ self.on_billing_setup_finished = on_billing_setup_finished
12
+ self.on_billing_service_disconnected = on_billing_service_disconnected
13
+
14
+ @java_method("(Lcom/android/billingclient/api/BillingResult;)V")
15
+ def onBillingSetupFinished(self, billing_result):
16
+ self.on_billing_setup_finished(billing_result)
17
+
18
+ @java_method("()V")
19
+ def onBillingServiceDisconnected(self):
20
+ self.on_billing_service_disconnected()
21
+
22
+
23
+ class BillingConfigResponseListener(PythonJavaClass):
24
+ __javainterfaces__ = ["com/android/billingclient/api/BillingConfigResponseListener"]
25
+ __javacontext__ = "app"
26
+
27
+ def __init__(self, callback):
28
+ self.callback = callback
29
+
30
+ @java_method("(Lcom/android/billingclient/api/BillingResult;Lcom/android/billingclient/api/BillingConfig;)V")
31
+ def onBillingConfigResponse(self, billing_result, billing_config):
32
+ self.callback(billing_result, billing_config)
@@ -0,0 +1,15 @@
1
+ from jnius import PythonJavaClass, java_method
2
+
3
+ __all__ = ("ConsumeResponseListener", )
4
+
5
+
6
+ class ConsumeResponseListener(PythonJavaClass):
7
+ __javainterfaces__ = ["com/android/billingclient/api/ConsumeResponseListener"]
8
+ __javacontext__ = "app"
9
+
10
+ def __init__(self, callback):
11
+ self.callback = callback
12
+
13
+ @java_method("(Lcom/android/billingclient/api/BillingResult;Ljava/lang/String;)V")
14
+ def onConsumeResponse(self, billing_result, purchase_token):
15
+ self.callback(billing_result, purchase_token)
@@ -0,0 +1,15 @@
1
+ from jnius import PythonJavaClass, java_method
2
+
3
+ __all__ = ("ProductDetailsResponseListener", )
4
+
5
+
6
+ class ProductDetailsResponseListener(PythonJavaClass):
7
+ __javainterfaces__ = ["com/android/billingclient/api/ProductDetailsResponseListener"]
8
+ __javacontext__ = "app"
9
+
10
+ def __init__(self, callback):
11
+ self.callback = callback
12
+
13
+ @java_method("(Lcom/android/billingclient/api/BillingResult;Ljava/util/List;)V")
14
+ def onProductDetailsResponse(self, billing_result, product_details_list):
15
+ self.callback(billing_result, product_details_list)
@@ -0,0 +1,16 @@
1
+ __all__ = ("PurchasesUpdatedListener", )
2
+
3
+ from jnius import PythonJavaClass, java_method
4
+ from sjbillingclient import is_jnull
5
+
6
+
7
+ class PurchasesUpdatedListener(PythonJavaClass):
8
+ __javainterfaces__ = ["com/android/billingclient/api/PurchasesUpdatedListener"]
9
+ __javacontext__ = "app"
10
+
11
+ def __init__(self, callback):
12
+ self.callback = callback
13
+
14
+ @java_method("(Lcom/android/billingclient/api/BillingResult;Ljava/util/List;)V")
15
+ def onPurchasesUpdated(self, billing_result, purchases):
16
+ self.callback(billing_result, is_jnull(purchases), purchases)
@@ -0,0 +1,139 @@
1
+ from jnius import autoclass, JavaException
2
+ from sjbillingclient.jclass.acknowledge import AcknowledgePurchaseParams
3
+ from sjbillingclient.jclass.billing import BillingClient as SJBillingClient, ProductType, ProductDetailsParams, \
4
+ BillingFlowParams
5
+ from android.activity import _activity as activity # noqa
6
+ from sjbillingclient.jclass.consume import ConsumeParams
7
+ from sjbillingclient.jclass.queryproduct import QueryProductDetailsParams, QueryProductDetailsParamsProduct
8
+ from sjbillingclient.jinterface.acknowledge import AcknowledgePurchaseResponseListener
9
+ from sjbillingclient.jinterface.billing import BillingClientStateListener
10
+ from sjbillingclient.jinterface.consume import ConsumeResponseListener
11
+ from sjbillingclient.jinterface.product import ProductDetailsResponseListener
12
+ from sjbillingclient.jinterface.purchases import PurchasesUpdatedListener
13
+
14
+
15
+ class BillingClient:
16
+ __billing_client = None
17
+ __purchase_update_listener = None
18
+ __billing_client_state_listener = None
19
+ __product_details_response_listener = None
20
+ __consume_response_listener = None
21
+ __acknowledge_purchase_response_listener = None
22
+
23
+ def __init__(self, on_purchases_updated):
24
+ self.__purchase_update_listener = PurchasesUpdatedListener(on_purchases_updated)
25
+
26
+ self.__billing_client = (
27
+ SJBillingClient.newBuilder(activity.context)
28
+ .setListener(self.__purchase_update_listener)
29
+ .enablePendingPurchases()
30
+ .build()
31
+ )
32
+
33
+ def start_connection(self, on_billing_setup_finished, on_billing_service_disconnected):
34
+ self.__billing_client_state_listener = BillingClientStateListener(
35
+ on_billing_setup_finished,
36
+ on_billing_service_disconnected
37
+ )
38
+ self.__billing_client.startConnection(self.__billing_client_state_listener)
39
+
40
+ def query_product_details_async(self, product_type, products_ids: list, on_product_details_response):
41
+ List = autoclass("java.util.List")
42
+ queryProductDetailsParams = (
43
+ QueryProductDetailsParams.newBuilder()
44
+ .setProductList(
45
+ List.of(*[
46
+ QueryProductDetailsParamsProduct.newBuilder()
47
+ .setProductId(product_id)
48
+ .setProductType(product_type)
49
+ .build()
50
+ for product_id in products_ids
51
+ ])
52
+ )
53
+ .build()
54
+ )
55
+
56
+ self.__product_details_response_listener = ProductDetailsResponseListener(on_product_details_response)
57
+
58
+ self.__billing_client.queryProductDetailsAsync(
59
+ queryProductDetailsParams,
60
+ self.__product_details_response_listener
61
+ )
62
+
63
+ @staticmethod
64
+ def get_product_details(product_details, product_type):
65
+ details = []
66
+ if product_type == ProductType.SUBS:
67
+ offer_details = product_details.getSubscriptionOfferDetails()
68
+ for offer in offer_details:
69
+ pricing_phase = offer.getPricingPhase().getPricingPhaseList().get(0)
70
+ details.append({
71
+ "formatted_price": pricing_phase.getFormattedPrice(),
72
+ "price_amount_micros": pricing_phase.getPriceAmountMicros,
73
+ "price_currency_code": pricing_phase.getPriceCurrencyCode(),
74
+ })
75
+ return details
76
+ elif product_type == ProductType.INAPP:
77
+ offer_details = product_details.getOneTimePurchaseOfferDetails()
78
+ details.append({
79
+ "formatted_price": offer_details.getFormattedPrice(),
80
+ "price_amount_micros": offer_details.getPriceAmountMicros,
81
+ "price_currency_code": offer_details.getPriceCurrencyCode(),
82
+ })
83
+ return details
84
+ raise Exception("product_type not supported. Must be one of `ProductType.SUBS`, `ProductType.INAPP`")
85
+
86
+ def launch_billing_flow(self, product_details: list, offer_token: str = None):
87
+
88
+ product_details_param_list = []
89
+
90
+ for product_detail in product_details:
91
+ params = ProductDetailsParams.newBuilder()
92
+ params.setProductDetails(product_detail)
93
+ if product_detail.getProductType() == ProductType.SUBS:
94
+ if not offer_token:
95
+ offer_list = product_detail.getSubscriptionOfferDetails()
96
+ if not offer_list or offer_list.isEmpty():
97
+ raise JavaException("You don't have a base plan")
98
+ base_plan_id = offer_list.get(0).getBasePlanId()
99
+ offer_token = offer_list.get(0).getOfferToken()
100
+ if not base_plan_id:
101
+ raise JavaException("You don't have a base plan id")
102
+ params.setOfferToken(offer_token)
103
+ else:
104
+ params.setOfferToken(offer_token)
105
+ product_details_param_list.append(params.build())
106
+ List = autoclass("java.util.List")
107
+
108
+ billing_flow_params = (
109
+ BillingFlowParams.newBuilder()
110
+ .setProductDetailsParamsList(List.of(*product_details_param_list))
111
+ .build()
112
+ )
113
+
114
+ billing_result = self.__billing_client.launchBillingFlow(activity, billing_flow_params)
115
+ return billing_result
116
+
117
+ def consume_async(self, purchase, on_consume_response):
118
+ consume_params = (
119
+ ConsumeParams.newBuilder()
120
+ .setPurchaseToken(purchase.getPurchaseToken())
121
+ .build()
122
+ )
123
+ self.__consume_response_listener = ConsumeResponseListener(on_consume_response)
124
+ self.__billing_client.consumeAsync(consume_params, self.__consume_response_listener)
125
+
126
+ def acknowledge_purchase(self, purchase_token, on_acknowledge_purchase_response):
127
+ acknowledge_purchase_params = (
128
+ AcknowledgePurchaseParams.newBuilder()
129
+ .setPurchaseToken(purchase_token)
130
+ .build()
131
+ )
132
+
133
+ self.__acknowledge_purchase_response_listener = AcknowledgePurchaseResponseListener(
134
+ on_acknowledge_purchase_response
135
+ )
136
+ self.__billing_client.acknowledgePurchase(
137
+ acknowledge_purchase_params,
138
+ self.__acknowledge_purchase_response_listener
139
+ )