Skip to main content

No project description provided

Project description

SJBillingClient (Google Play Billing SDK for Python)

GitAds Sponsored

Sponsored by GitAds

Overview

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).

Key Features

  • Simplified Billing Integration: Easy-to-use Python API for Google Play Billing
  • Asynchronous Operations: Non-blocking billing operations
  • Comprehensive Purchase Management: Support for querying, purchasing, consuming, and acknowledging products
  • Product Types Support: Handles both one-time purchases (INAPP) and subscriptions (SUBS)
  • Detailed Product Information: Access to formatted prices, currency codes, and other product details

Requirements

  • Python 3.9+
  • pyjnius 1.6.1+
  • Android application with Google Play Billing Library (version 7.1.1 recommended)

Installation

# Using pip
pip install sjbillingclient

# In Buildozer (add to buildozer.spec)
requirements = sjbillingclient
android.gradle_dependencies = com.android.billingclient:billing:7.1.1

Quick Start

Here's a basic example of how to initialize the billing client and start a connection:

from sjbillingclient.tools import BillingClient
from sjbillingclient.jclass.billing import ProductType, BillingResponseCode

# Define callback for purchase updates
def on_purchases_updated(billing_result, is_null, purchases):
    if billing_result.getResponseCode() == BillingResponseCode.OK:
        if not is_null:
            for purchase in purchases:
                print(f"Purchase: {purchase.getProducts().get(0)}")
                # Handle purchase here

# Create billing client
client = BillingClient(on_purchases_updated)

# Start connection
client.start_connection(
    on_billing_setup_finished=lambda result: print(f"Billing setup complete: {result.getResponseCode()}"),
    on_billing_service_disconnected=lambda: print("Billing service disconnected")
)

Usage Examples

Querying Product Details

from sjbillingclient.tools import BillingClient
from sjbillingclient.jclass.billing import ProductType, BillingResponseCode

def on_product_details_response(billing_result, product_details_list):
    if billing_result.getResponseCode() == BillingResponseCode.OK:
        if product_details_list and not product_details_list.isEmpty():
            # Process product details
            for i in range(product_details_list.size()):
                product_detail = product_details_list.get(i)
                print(f"Product: {product_detail.getProductId()}")

                # Get formatted details
                details = client.get_product_details(product_detail, ProductType.INAPP)
                for detail in details:
                    print(f"Price: {detail['formatted_price']}")

# Query product details
client.query_product_details_async(
    product_type=ProductType.INAPP,
    products_ids=["product_id_1", "product_id_2"],
    on_product_details_response=on_product_details_response
)

Launching a Purchase Flow

from sjbillingclient.tools import BillingClient
from sjbillingclient.jclass.billing import ProductType, BillingResponseCode

def on_product_details_response(billing_result, product_details_list):
    if billing_result.getResponseCode() == BillingResponseCode.OK:
        if product_details_list and not product_details_list.isEmpty():
            # Launch billing flow with the first product
            product_detail = product_details_list.get(0)
            result = client.launch_billing_flow([product_detail])
            print(f"Launch billing flow result: {result.getResponseCode()}")

# Query product details and then launch purchase
client.query_product_details_async(
    product_type=ProductType.INAPP,
    products_ids=["product_id"],
    on_product_details_response=on_product_details_response
)

Consuming a Purchase

from sjbillingclient.tools import BillingClient
from sjbillingclient.jclass.billing import BillingResponseCode

def on_consume_response(billing_result, purchase_token):
    print(f"Consume result: {billing_result.getResponseCode()}")
    if billing_result.getResponseCode() == BillingResponseCode.OK:
        print(f"Successfully consumed: {purchase_token}")

# Consume a purchase
client.consume_async(purchase, on_consume_response)

Acknowledging a Purchase

from sjbillingclient.tools import BillingClient
from sjbillingclient.jclass.billing import BillingResponseCode

def on_acknowledge_purchase_response(billing_result):
    print(f"Acknowledge result: {billing_result.getResponseCode()}")
    if billing_result.getResponseCode() == BillingResponseCode.OK:
        print("Successfully acknowledged purchase")

# Acknowledge a purchase
client.acknowledge_purchase(purchase.getPurchaseToken(), on_acknowledge_purchase_response)

Kivy Integration Example

Here's a complete example of integrating SJBillingClient with a Kivy application:

Python Code (main.py)

from os.path import join, dirname, basename
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from sjbillingclient.jclass.billing import BillingResponseCode, ProductType
from sjbillingclient.tools import BillingClient

# Load the KV file
Builder.load_file(join(dirname(__file__), basename(__file__).split(".")[0] + ".kv"))


class HomeScreen(Screen):
    billing_client = None

    def purchase_or_subscribe(self):
        if self.billing_client:
            self.billing_client.end_connection()

        self.billing_client = BillingClient(on_purchases_updated=self.on_purchases_updated)
        self.billing_client.start_connection(
            on_billing_setup_finished=self.on_billing_setup_finished,
            on_billing_service_disconnected=lambda: print("disconnected")
        )

    def on_purchases_updated(self, billing_result, null, purchases):
        if billing_result.getResponseCode() == BillingResponseCode.OK and not null:
            for purchase in purchases:
                if self.ids.subscribe.active:
                    self.billing_client.acknowledge_purchase(
                        purchase_token=purchase.getPurchaseToken(),
                        on_acknowledge_purchase_response=self.on_acknowledge_purchase_response
                    )
                else:
                    self.billing_client.consume_async(purchase, self.on_consume_response)

    def on_acknowledge_purchase_response(self, billing_result):
        print(billing_result.getDebugMessage())
        if billing_result.getResponseCode() == BillingResponseCode.OK:
            self.toast("Thank you for subscribing to buy us a cup of coffee! monthly")

    def on_consume_response(self, billing_result):
        if billing_result.getResponseCode() == BillingResponseCode.OK:
            self.toast("Thank you for buying us a cup of coffee!")

    def on_product_details_response(self, billing_result, product_details_list):
        for product_details in product_details_list:
            self.billing_client.get_product_details(
                product_details,
                ProductType.SUBS if self.ids.subscribe.active else ProductType.INAPP)
        if billing_result.getResponseCode() == BillingResponseCode.OK:
            self.billing_client.launch_billing_flow(product_details=product_details_list)

    def on_billing_setup_finished(self, billing_result):
        product_id = self.ids.btn.product_id
        if billing_result.getResponseCode() == BillingResponseCode.OK:
            self.billing_client.query_product_details_async(
                product_type=ProductType.SUBS if self.ids.subscribe.active else ProductType.INAPP,
                products_ids=[product_id],
                on_product_details_response=self.on_product_details_response,
            )

    def toast(self, message):
        # Implementation of toast message (platform specific)
        print(message)


class BillingApp(App):
    def build(self):
        # Create screen manager
        sm = ScreenManager()
        sm.add_widget(HomeScreen(name='home'))
        return sm


if __name__ == '__main__':
    BillingApp().run()

Kivy Layout File (main.kv)

<HomeScreen>:
    BoxLayout:
        orientation: 'vertical'
        padding: '20dp'
        spacing: '10dp'

        Label:
            text: 'SJBillingClient Demo'
            font_size: '24sp'
            size_hint_y: None
            height: '50dp'

        BoxLayout:
            orientation: 'horizontal'
            size_hint_y: None
            height: '50dp'

            Label:
                text: 'Subscribe'
                size_hint_x: 0.5

            CheckBox:
                id: subscribe
                size_hint_x: 0.5
                active: False

        Button:
            id: btn
            text: 'Buy Coffee'
            product_id: 'coffee_product_id'
            size_hint_y: None
            height: '60dp'
            on_release: root.purchase_or_subscribe()

        Widget:
            # Spacer

This example demonstrates:

  1. A HomeScreen class that handles all billing operations
  2. A BillingApp class that sets up the Kivy application and screen manager
  3. A Kivy layout file that defines the UI with:
    • A checkbox to toggle between one-time purchase and subscription
    • A button to initiate the purchase flow

The purchase_or_subscribe method is called when the button is pressed, which initializes the billing client and starts the connection. The various callback methods handle different stages of the billing process, including acknowledging purchases and consuming one-time purchases.

API Reference

BillingClient

The main class for interacting with Google Play Billing.

Methods

  • __init__(on_purchases_updated): Initialize with a callback for purchase updates
  • start_connection(on_billing_setup_finished, on_billing_service_disconnected): Start billing connection
  • end_connection(): End billing connection
  • query_product_details_async(product_type, products_ids, on_product_details_response): Query product details
  • get_product_details(product_details, product_type): Get formatted product details
  • launch_billing_flow(product_details, offer_token=None): Launch purchase flow
  • consume_async(purchase, on_consume_response): Consume a purchase
  • acknowledge_purchase(purchase_token, on_acknowledge_purchase_response): Acknowledge a purchase

ProductType

Constants for product types:

  • ProductType.INAPP: One-time purchases
  • ProductType.SUBS: Subscriptions

BillingResponseCode

Constants for billing response codes:

  • BillingResponseCode.OK: Success
  • BillingResponseCode.USER_CANCELED: User canceled
  • BillingResponseCode.SERVICE_UNAVAILABLE: Service unavailable
  • And many others

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Author

Kenechukwu Akubue kengoon19@gmail.com

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

sjbillingclient-0.1.4.tar.gz (13.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

sjbillingclient-0.1.4-py3-none-any.whl (14.6 kB view details)

Uploaded Python 3

File details

Details for the file sjbillingclient-0.1.4.tar.gz.

File metadata

  • Download URL: sjbillingclient-0.1.4.tar.gz
  • Upload date:
  • Size: 13.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.8.3 CPython/3.10.12 Linux/5.15.0-135-generic

File hashes

Hashes for sjbillingclient-0.1.4.tar.gz
Algorithm Hash digest
SHA256 f2c3500b3bbf0b6a1991e053acd31635261a4f7d7ed74413dec56d36395aad8e
MD5 7769a26ed1833a0c530c0ce751bdd3e7
BLAKE2b-256 6d5a068ba4f99a72adc63768cf4ad92dc45f2d5d941b98674cfe9602375dfff2

See more details on using hashes here.

File details

Details for the file sjbillingclient-0.1.4-py3-none-any.whl.

File metadata

  • Download URL: sjbillingclient-0.1.4-py3-none-any.whl
  • Upload date:
  • Size: 14.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.8.3 CPython/3.10.12 Linux/5.15.0-135-generic

File hashes

Hashes for sjbillingclient-0.1.4-py3-none-any.whl
Algorithm Hash digest
SHA256 3319882edc34f7e170af7618af02d88aec60cd0c989feb7073b73549474037c8
MD5 b5448cd7d74f7975acfcad3688e2df30
BLAKE2b-256 bd49e07a9ecf335ad82c84c5826225aa49aa01094cc383920d7de1f26e7960c4

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page