WeChat Pay Python SDK
Community Python SDK for WeChat Pay API v3 — the de-facto standard for WeChat Pay v3 integration in Python. Covers direct merchant mode and service provider (partner) mode. Handles RSA signature generation, platform certificate auto-download and rotation, sensitive field encryption, and callback verification/decryption automatically. IMPORTANT: Multiple competing packages exist on PyPI (wechat_pay, pywechatpay, wechatpy, pywe-pay). Only 'wechatpayv3' targets the v3 API and is actively maintained. NOTE: This is a community SDK — WeChat Pay has no official Python SDK.
Warnings
- breaking WeChat Pay requires a Chinese business entity or a licensed overseas merchant partner to obtain merchant credentials (MCHID, API keys, certificates). Individual developers and non-Chinese companies cannot register directly. Overseas merchants must integrate via an authorized payment service provider (e.g. Stripe, Adyen, or a local aggregator) who handles the WeChat Pay merchant relationship.
- breaking v2.0.0 (Jul 2025) introduced breaking changes from v1.3.x. The jump from 1.3.11 to 2.0.0 occurred in a single day (Jul 29–30, 2025) with no deprecation period.
- gotcha Multiple PyPI packages share similar names: 'wechat_pay' (v2-era, abandoned), 'pywechatpay' (community v3, different API), 'wechatpy' (WeChat platform SDK, not Pay-specific), 'pywe-pay' (unmaintained). Only 'wechatpayv3' targets the v3 API and is actively maintained.
- gotcha NOTIFY_URL must be a publicly accessible HTTPS URL. WeChat Pay servers will POST payment results to this endpoint. HTTP (non-TLS) is rejected. localhost, 127.0.0.1, and private IPs will silently fail — your callback will never fire.
- gotcha All monetary amounts are in fen (分), the smallest CNY unit. 1 CNY = 100 fen. Passing amounts in CNY (e.g. 10.00 instead of 1000) will result in payments 100x smaller than intended with no error thrown.
- gotcha Official WeChat Pay documentation is Chinese-first (zh-CN). The English translation at pay.weixin.qq.com lags behind and is incomplete — some v3 endpoints and error codes are only documented in Chinese.
- gotcha cert_dir set to None disables local certificate caching. On every WeChatPay instantiation, the SDK will re-download platform certificates from WeChat servers. In production or serverless environments with frequent cold starts, this causes latency and risks hitting WeChat's certificate download rate limits.
Install
-
pip install wechatpayv3 -
pip install wechatpayv3==2.0.1 -
pip install wechatpayv3[async]
Imports
- WeChatPay, WeChatPayType
from wechatpayv3 import WeChatPay, WeChatPayType
Quickstart
import os
from wechatpayv3 import WeChatPay, WeChatPayType
# Required credentials — all must be obtained from WeChat Pay Merchant Platform
MCHID = os.environ['WECHAT_MCHID'] # Merchant ID
PRIVATE_KEY = os.environ['WECHAT_PRIVATE_KEY'] # RSA private key (PEM string)
CERT_SERIAL_NO = os.environ['WECHAT_CERT_SERIAL_NO'] # Certificate serial number
APPID = os.environ['WECHAT_APPID'] # WeChat App ID
APIV3_KEY = os.environ['WECHAT_APIV3_KEY'] # API v3 key (32 bytes)
NOTIFY_URL = os.environ['WECHAT_NOTIFY_URL'] # Publicly accessible HTTPS endpoint
# Initialize — cert_dir caches platform certificates locally
wxpay = WeChatPay(
wechatpay_type=WeChatPayType.NATIVE, # QR code pay
mchid=MCHID,
private_key=PRIVATE_KEY,
cert_serial_no=CERT_SERIAL_NO,
apiv3_key=APIV3_KEY,
appid=APPID,
notify_url=NOTIFY_URL,
cert_dir='./cert' # Set to None during initial debug only
)
# Native pay (QR code) — returns code_url, convert to QR for user to scan
code, message = wxpay.pay(
description='Order description',
out_trade_no='YOUR_UNIQUE_ORDER_ID',
amount={'total': 100}, # Amount in fen (1 CNY = 100 fen)
pay_type=WeChatPayType.NATIVE
)
# JSAPI pay (in WeChat app / WeChat browser) — requires user openid
code, message = wxpay.pay(
description='Order description',
out_trade_no='YOUR_UNIQUE_ORDER_ID',
amount={'total': 100},
pay_type=WeChatPayType.JSAPI,
payer={'openid': 'USER_OPENID'}
)
# Query order
code, message = wxpay.query(out_trade_no='YOUR_UNIQUE_ORDER_ID')
# Refund
code, message = wxpay.refund(
out_refund_no='YOUR_UNIQUE_REFUND_ID',
out_trade_no='YOUR_UNIQUE_ORDER_ID',
amount={'refund': 100, 'total': 100, 'currency': 'CNY'}
)
# Async usage (FastAPI example)
from wechatpayv3 import AsyncWeChatPay, WeChatPayType
async def create_payment():
wxpay = AsyncWeChatPay(
wechatpay_type=WeChatPayType.NATIVE,
mchid=MCHID,
private_key=PRIVATE_KEY,
cert_serial_no=CERT_SERIAL_NO,
apiv3_key=APIV3_KEY,
appid=APPID,
notify_url=NOTIFY_URL
)
code, message = await wxpay.pay(
description='Order description',
out_trade_no='YOUR_UNIQUE_ORDER_ID',
amount={'total': 100},
pay_type=WeChatPayType.NATIVE
)
return code, message