# WeChat Payment Integration in Mini-Shop Server: A Complete Server-Side Implementation Guide

> Implement WeChat Pay server-side with this complete guide. Learn the three-step workflow for order validation, payment parameter generation, and notification processing in your Mini-Shop server.

- Repository: [A粒麦子/mini-shop-server](https://github.com/allen7d/mini-shop-server)
- Tags: how-to-guide
- Published: 2026-02-24

---

**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:

```python

# 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`](https://github.com/allen7d/mini-shop-server/blob/main/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:

```python

# 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`](https://github.com/allen7d/mini-shop-server/blob/main/pyproject.toml)) to communicate with WeChat:

```python
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`](https://github.com/allen7d/mini-shop-server/blob/main/app/service/pay.py) first verifies the WeChat response succeeded:

```python
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:

```python
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`](https://github.com/allen7d/mini-shop-server/blob/main/app/api/v1/pay.py), the `receive_notify` function processes WeChat's XML callback:

```python
@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`](https://github.com/allen7d/mini-shop-server/blob/main/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`](https://github.com/allen7d/mini-shop-server/blob/main/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`](https://github.com/allen7d/mini-shop-server/blob/main/app/api/v1/pay.py)**: Flask route definitions for `/v1/pay/pre_order` and `/v1/pay/notify` endpoints.
- **[`app/models/order.py`](https://github.com/allen7d/mini-shop-server/blob/main/app/models/order.py)**: Defines the `Order` model with `order_status` field using `OrderStatusEnum`.
- **[`app/models/user.py`](https://github.com/allen7d/mini-shop-server/blob/main/app/models/user.py)**: Stores the WeChat `openid` required for JSAPI payments.
- **[`app/service/order.py`](https://github.com/allen7d/mini-shop-server/blob/main/app/service/order.py)**: Provides `check_order_stock()` utility to prevent overselling during payment initiation.
- **[`pyproject.toml`](https://github.com/allen7d/mini-shop-server/blob/main/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`](https://github.com/allen7d/mini-shop-server/blob/main/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`](https://github.com/allen7d/mini-shop-server/blob/main/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.