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:

  1. Order existence and ownership via __check_order_valid()
  2. Payment status verification ensuring OrderStatusEnum.UNPAID
  3. 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 containing PayService class with pay(), __make_wx_pre_order(), and __get_pay_signature() methods.
  • app/api/v1/pay.py: Flask route definitions for /v1/pay/pre_order and /v1/pay/notify endpoints.
  • app/models/order.py: Defines the Order model with order_status field using OrderStatusEnum.
  • app/models/user.py: Stores the WeChat openid required for JSAPI payments.
  • app/service/order.py: Provides check_order_stock() utility to prevent overselling during payment initiation.
  • pyproject.toml: Declares the wechatpy dependency enabling WeChat API communication.

Summary

  • Validation first: The server validates order ownership, payment status, and inventory before contacting WeChat.
  • SDK-based communication: The wechatpy library handles the Unified Order API calls in app/service/pay.py.
  • openid requirement: JSAPI payments require the user's WeChat openid retrieved from the User model.
  • 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →