A python library for integrating with PhonePe APIs.
- Retry mechanism removed. The SDK no longer retries any HTTP call (including GET). The
should_retryconstructor parameter has been removed fromStandardCheckoutClient,CustomCheckoutClient, andSubscriptionClient- passing it now raises aTypeError. - Client construction can now raise. The SDK fetches its OAuth token immediately at
construction (a single, non-blocking attempt) instead of waiting for the first API call.
Genuine configuration problems (e.g. invalid credentials) now fail fast and
get_instance(...)/the constructor raises immediately, where previously construction always succeeded regardless of credential validity. See the Quick start note below for details - transient failures do NOT raise or block; they're retried automatically in the background instead. - New: configurable connection pooling/timeouts via
HttpClientConfig(see Connection pool & timeout tuning) and aclose()method on every client to release resources cleanly.
Requires python 3.9 or later
pip install phonepe-pg-sdk-python
To get your keys, please visit the Merchant Onboarding of PhonePe PG: Merchant Onboarding
You will need three things to get started: client-id, client-secret and client-version.
Create an instance of the StandardCheckoutClient class:
from phonepe.sdk.pg.payments.v2.standard_checkout_client import StandardCheckoutClient
from phonepe.sdk.pg.env import Env
client_id = "<YOUR_CLIENT_ID>"
client_secret = "<YOUR_CLIENT_SECRET>"
client_version = 1 # Insert your client version here
env = Env.SANDBOX # Change to Env.PRODUCTION when you go live
standard_phonepe_client = StandardCheckoutClient.get_instance(client_id=client_id,
client_secret=client_secret,
client_version=client_version,
env=env)Note: Client construction fetches an OAuth token immediately (a single, non-blocking attempt) rather than waiting for the first API call. A genuine configuration problem (e.g. invalid credentials) fails fast and
get_instance(...)/the constructor raises immediately; a transient failure (network blip, 5xx, rate-limiting) does NOT block construction or raise - it's retried automatically in the background instead. Once constructed, the token is kept fresh automatically in the background for the lifetime of the client - see Connection pool & timeout tuning below forclose()and other tunable behavior.
To init a pay request, we make a request object using StandardCheckoutPayRequest.build_request build_request.
from uuid import uuid4
from phonepe.sdk.pg.payments.v2.models.request.standard_checkout_pay_request import StandardCheckoutPayRequest
unique_order_id = str(uuid4())
ui_redirect_url = "https://www.merchant.com/redirect"
amount = 100
standard_pay_request = StandardCheckoutPayRequest.build_request(merchant_order_id=unique_order_id,
amount=amount,
redirect_url=ui_redirect_url)
standard_pay_response = standard_phonepe_client.pay(standard_pay_request)
checkout_page_url = standard_pay_response.redirect_urlThe data will be in a StandardCheckoutPayResponse object.
The checkout_page_url you get can be handled by redirecting the user to that url on the front end.
View the state for the order we just initiated.
unique_order_id = "INSERT_YOUR_UNIQUE_ORDER_ID"
order_status_response = standard_phonepe_client.get_order_status(merchant_order_id=unique_order_id)
order_state = order_status_response.stateYou will get the data OrderStatusResponse object.
For more details, please visit: https://developer.phonepe.com
Every client accepts an optional http_client_config argument on both its constructor and get_instance(...), letting
you tune the underlying HTTP connection pool and timeouts per merchant/client instance:
from phonepe.sdk.pg.common.configs.http_client_config import HttpClientConfig
from phonepe.sdk.pg.payments.v2.standard_checkout_client import StandardCheckoutClient
from phonepe.sdk.pg.env import Env
http_client_config = HttpClientConfig(
pool_size=10, # max pooled (kept-alive) connections per host
keep_alive_seconds=60, # proactively recycle connections idle longer than this
connect_timeout_seconds=3, # max time to establish the TCP/TLS connection
read_timeout_seconds=30, # max time to wait for a response once the request is sent
pool_block=True, # wait for a free pooled connection instead of overflowing the pool
)
standard_phonepe_client = StandardCheckoutClient.get_instance(
client_id=client_id,
client_secret=client_secret,
client_version=client_version,
env=env,
http_client_config=http_client_config,
)If http_client_config is omitted, the SDK uses the defaults shown above (pool_size=10,
keep_alive_seconds=60, connect_timeout_seconds=3, read_timeout_seconds=30,
pool_block=True).
Why these four settings trade off against each other:
pool_sizecaps how many connections are kept alive per host. A merchant sending many concurrent requests benefits from a larger pool so requests don't queue up waiting for a free connection; a merchant sending only the occasional request (e.g. one every several seconds) gains nothing from a large pool - a small value (2-4) is enough, since most of those connections would otherwise sit idle.keep_alive_secondsbounds how long a pooled connection can sit idle before the SDK proactively closes and replaces it with a fresh one, rather than risking handing a request a connection that a server/load balancer has already silently closed while idle (a scenario confirmed via repro testing against PhonePe's production environment). This is enforced both the moment a connection is next reused for a request and independently by a background sweep thread that periodically closes idle connections directly, so staleness is bounded even during a period with no request traffic at all.connect_timeout_seconds/read_timeout_secondsbound how long a single request is allowed to take establishing a connection vs. waiting for a response. A merchant with fast, reliable infrastructure can tighten these to fail faster on genuine problems; a merchant on slower/less reliable infrastructure (or calling latency-sensitive endpoints like autoPay APIs) may need to raiseread_timeout_secondsto avoid timing out on otherwise-successful, just-slow responses.pool_blockdecides what happens when allpool_sizeconnections are already busy and another request needs one. With the defaultTrue, the request waits for one to be released, sopool_sizeis a genuine cap on concurrent connections. WithFalse, the request instead opens an extra connection outside the pool and throws it away immediately after that single request - so a traffic burst pays a fresh TCP/TLS handshake per overflow request andpool_sizeno longer limits anything. Waiting cannot deadlock: every in-flight request releases its slot when it completes or hitsread_timeout_seconds, so that timeout (notconnect_timeout_seconds) bounds how long a request can wait for a slot. If you setpool_block=False, sizepool_sizefor your peak concurrency to keep overflow rare.
Every client exposes a close() method that releases pooled HTTP connections and stops the
SDK's background threads (token refresh, connection recycling, event publishing).
Calling close() is optional. All of these are daemon threads, so a long-lived server that
creates its client once and never closes it works exactly as before and still exits cleanly.
Call close() only when you want a deterministic, immediate release - short-lived processes
(tests, scripts, serverless invocations), or when you want to discard a client instance:
standard_phonepe_client.close()close() also removes the instance from the get_instance() cache, so a later get_instance()
call with the same arguments builds a fresh client instead of returning the closed one. It is
safe to call multiple times, and it waits for any in-flight event flush to finish.
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request