WeChat Payment Integration in Mini-Shop Server: A Complete Server-Side Implementation Guide
The Mini-Shop server implements WeChat Pay through a three-step workflow: validating orders via /v1/pay/pre_order, generating signed payment parameters using the wechatpy SDK, and processing asynchronous notifications at /v1/pay/notify.
The allen7d/mini-shop-server repository provides a Flask-based implementation of WeChat payment integration for e-commerce applications. This guide examines the actual source code to explain how the server validates orders, communicates with WeChat's Unified Order API, and handles payment callbacks securely.
Pre-Order Request Workflow
The payment process begins when the client initiates a pre-order request. The server validates the order state, verifies inventory, and prepares the WeChat API payload.
API Endpoint and PayService Initialization
The client POSTs to /v1/pay/pre_order with an order_id. The view instantiates PayService and invokes the pay() method:
# app/api/v1/pay.py
@api.route('/pre_order', methods=['POST'])
def get_pre_order():
order_id = request.json.get('order_id')
pay_service = PayService(order_id)
result = pay_service.pay()
return result
The PayService.__init__() stores the order ID, while pay() orchestrates three critical validation steps defined in app/service/pay.py:
- Order existence and ownership via
__check_order_valid() - Payment status verification ensuring
OrderStatusEnum.UNPAID - Stock availability check using
OrderService.check_order_stock()
Building the WeChat Unified Order
Once validation passes, __make_wx_pre_order() constructs the payload for WeChat's API. The method retrieves the user's openid from the User model and prepares the transaction data:
# app/service/pay.py - __make_wx_pre_order
user = User.query.filter_by(id=g.user.id).first_or_404()
openid = user.openid
wx_order_data = {
"body": "Mini-Shop Order",
"out_trade_no": self.order_no,
"total_fee": int(order_price * 100), # Convert Yuan to fen
"spbill_create_ip": request.remote_addr,
"notify_url": "https://api.example.com/v1/pay/notify",
"trade_type": "JSAPI",
"openid": openid,
}
The implementation relies on wechatpy==1.8.18 (declared in pyproject.toml) to communicate with WeChat:
from wechatpy.pay import WeChatPay
from app.config.setting import WECHAT_APPID, WECHAT_MCH_ID, WECHAT_API_KEY
wechat_pay = WeChatPay(
appid=WECHAT_APPID,
api_key=WECHAT_API_KEY,
mch_id=WECHAT_MCH_ID
)
wx_order = wechat_pay.order.create(**wx_order_data)
Generating the Client Payment Signature
After obtaining the prepay_id from WeChat, the server must generate a signature that the frontend JS SDK uses to invoke the payment dialog.
Signature Computation in __get_pay_signature
The __get_pay_signature() method in app/service/pay.py first verifies the WeChat response succeeded:
if wx_order['return_code'] != 'SUCCESS' or wx_order['result_code'] != 'SUCCESS':
raise RuntimeError("WeChat unified order failed")
It then constructs the signed parameters using MD5 hashing:
import hashlib
import time
import random
prepay_id = wx_order['prepay_id']
data = {
"appId": WECHAT_APPID,
"timeStamp": str(int(time.time())),
"nonceStr": ''.join(random.choices('abcdefghijklmnopqrstuvwxyz0123456789', k=32)),
"package": f"prepay_id={prepay_id}",
"signType": "MD5",
}
# Create signature string sorted by key
string = '&'.join(f"{k}={v}" for k, v in sorted(data.items()))
string += f"&key={WECHAT_API_KEY}"
data["paySign"] = hashlib.md5(string.encode('utf-8')).hexdigest().upper()
The frontend receives this dictionary to call WeixinJSBridge.invoke('getBrandWCPayRequest', ...).
Handling Asynchronous Payment Notifications
WeChat notifies the server of payment completion via the configured notify_url. The repository implements the notification endpoint at /v1/pay/notify.
Notification Endpoint Implementation
Located in app/api/v1/pay.py, the receive_notify function processes WeChat's XML callback:
@api.route('/notify', methods=['POST'])
def receive_notify():
# Current implementation returns Success() as placeholder
# Production code should:
# 1. Parse XML from request.data
# 2. Verify signature using WeChat API key
# 3. Update Order.order_status to OrderStatusEnum.PAID
return Success()
The intended production flow requires parsing the XML payload, extracting out_trade_no to locate the order in app/models/order.py, and persisting the PAID status. WeChat expects an XML response confirming receipt; failure to respond with SUCCESS triggers up to 15 retries over 24 hours.
Key Source Files and Architecture
Understanding the WeChat payment integration requires familiarity with these specific modules:
app/service/pay.py: Core business logic containingPayServiceclass withpay(),__make_wx_pre_order(), and__get_pay_signature()methods.app/api/v1/pay.py: Flask route definitions for/v1/pay/pre_orderand/v1/pay/notifyendpoints.app/models/order.py: Defines theOrdermodel withorder_statusfield usingOrderStatusEnum.app/models/user.py: Stores the WeChatopenidrequired for JSAPI payments.app/service/order.py: Providescheck_order_stock()utility to prevent overselling during payment initiation.pyproject.toml: Declares thewechatpydependency enabling WeChat API communication.
Summary
- Validation first: The server validates order ownership, payment status, and inventory before contacting WeChat.
- SDK-based communication: The
wechatpylibrary handles the Unified Order API calls inapp/service/pay.py. - openid requirement: JSAPI payments require the user's WeChat
openidretrieved from theUsermodel. - Dual signatures: The server generates one signature for WeChat API authentication and a second for the frontend JS SDK.
- Async confirmation: Payment completion is confirmed via XML callbacks to
/v1/pay/notify, requiring idempotent status updates.
Frequently Asked Questions
What Python library does the Mini-Shop server use for WeChat Pay?
The project uses wechatpy version 1.8.18, a Python SDK for WeChat services. The WeChatPay class from this library handles the Unified Order API calls and response parsing in app/service/pay.py.
How does the server prevent duplicate payments for the same order?
Before processing the pre-order request, PayService.pay() checks that Order.order_status equals OrderStatusEnum.UNPAID. If the order is already paid or cancelled, the validation fails and the request returns an error before contacting WeChat's API.
Why does the code multiply the price by 100 when calling WeChat?
WeChat's API expects amounts in fen (the smallest currency unit), while the application stores prices in Yuan. The conversion int(order_price * 100) ensures the total_fee parameter meets WeChat's requirements for the Unified Order API.
What should the production implementation of /v1/pay/notify include?
The endpoint must parse the XML payload from request.data, verify the cryptographic signature using the WeChat API key, locate the order via out_trade_no, update order_status to OrderStatusEnum.PAID, and return an XML response containing <return_code>SUCCESS</return_code>. The current repository contains a placeholder that simply returns Success() without processing.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →