Files
Genarrative/.codex/skills/wechatpay-basic-payment/scripts/商户/查询订单.py
2026-05-15 03:35:30 +08:00

181 lines
7.3 KiB
Python
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
查询订单(基础支付 - 商户订单号查单)
API文档https://pay.weixin.qq.com/doc/v3/merchant/4012791916
依赖Python3macOS/Linux 自带)
无需 pip install 任何第三方库。
所有参数通过命令行传入,脚本内不硬编码任何配置。
模型负责交互式收集参数,拼装命令行调用本脚本。
签名模式用户在自己的服务器上完成签名后将签名值Base64、时间戳、随机串传入
脚本直接使用这些值构造 Authorization 头并发送请求。
用法:
python3 查询订单.py \
--mchid <商户号> \
--serial-no <API证书序列号> \
--signature <Base64签名值> \
--timestamp <签名时使用的时间戳> \
--nonce-str <签名时使用的随机串> \
--wechat-pay-public-key-id <微信支付公钥ID> \
--out-trade-no <商户订单号>
"""
import argparse
import json
import sys
from urllib.request import Request, urlopen
from urllib.error import HTTPError, URLError
from urllib.parse import quote
def build_authorization(mchid, serial_no, signature, timestamp, nonce_str):
return (
f'WECHATPAY2-SHA256-RSA2048 '
f'mchid="{mchid}",'
f'nonce_str="{nonce_str}",'
f'signature="{signature}",'
f'timestamp="{timestamp}",'
f'serial_no="{serial_no}"'
)
def print_order_analysis(data):
"""解析订单关键信息。"""
print("\n========== 订单关键信息 ==========")
print(f" 商户订单号: {data.get('out_trade_no', '-')}")
print(f" 微信订单号: {data.get('transaction_id', '-')}")
print(f" 交易状态: {data.get('trade_state', '-')}")
print(f" 交易状态描述: {data.get('trade_state_desc', '-')}")
print(f" 交易类型: {data.get('trade_type', '-')}")
print(f" 商户号: {data.get('mchid', '-')}")
print(f" appid: {data.get('appid', '-')}")
amount = data.get("amount", {})
print(f" 订单金额(分): {amount.get('total', '-')}")
print(f" 用户支付(分): {amount.get('payer_total', '-')}")
if data.get("success_time"):
print(f" 支付完成时间: {data['success_time']}")
print("==================================")
state = data.get("trade_state", "")
print("\n---------- 自动诊断 ----------")
if state == "SUCCESS":
print("✅ 订单已支付成功。")
print(f" → 微信订单号: {data.get('transaction_id', '-')}")
print(" → 如需退款,请使用此 transaction_id 调用退款接口。")
elif state == "NOTPAY":
print("⚠️ 订单未支付NOTPAY")
print(" → 用户尚未完成支付,或 prepay_id 已过期2小时需重新下单。")
print(" → 如不再需要此订单,建议调用关单接口关闭。")
elif state == "CLOSED":
print("⚠️ 订单已关闭CLOSED")
print(" → 订单已被商户主动关单或超时自动关闭,需用新的 out_trade_no 重新下单。")
elif state == "REFUND":
print(" 订单转入退款REFUND")
print(" → 已有退款发起,可用退款查询脚本确认退款状态。")
elif state == "USERPAYING":
print("⏳ 用户支付中USERPAYING常见于付款码支付。")
print(" → 需轮询查单直到终态SUCCESS/CLOSED超时后调用撤销接口。")
elif state == "PAYERROR":
print("❌ 支付失败PAYERROR")
print(" → 付款码场景需调用撤销接口,其他场景需重新下单。")
else:
print(f" 交易状态: {state}")
print("-------------------------------")
def main():
parser = argparse.ArgumentParser(
description="查询订单(基础支付 - 商户订单号查单)",
formatter_class=argparse.RawDescriptionHelpFormatter,
)
parser.add_argument("--mchid", required=True, help="商户号")
parser.add_argument("--serial-no", required=True, help="API证书序列号")
parser.add_argument("--wechat-pay-public-key-id", required=True, help="微信支付公钥ID")
sign_group = parser.add_argument_group("签名参数")
sign_group.add_argument("--signature", required=True, help="用户在自己服务器上生成的 Base64 签名值")
sign_group.add_argument("--timestamp", required=True, help="签名时使用的时间戳10位Unix秒")
sign_group.add_argument("--nonce-str", required=True, help="签名时使用的随机字符串")
parser.add_argument("--out-trade-no", required=True, help="商户订单号")
args = parser.parse_args()
uri = f"/v3/pay/transactions/out-trade-no/{quote(args.out_trade_no, safe='')}?mchid={quote(args.mchid, safe='')}"
full_url = f"https://api.mch.weixin.qq.com{uri}"
method = "GET"
body = ""
sign_str = f"{method}\n{uri}\n{args.timestamp}\n{args.nonce_str}\n{body}\n"
print("========== 预签名核对 ==========")
print("脚本计算的待签名串(请与您在服务器上使用的待签名串核对):")
print("--- 开始 ---")
print(sign_str, end="")
print("--- 结束 ---")
print("如果上述待签名串与您签名时使用的不一致,签名验证将失败。")
print("================================\n")
authorization = build_authorization(
mchid=args.mchid,
serial_no=args.serial_no,
signature=args.signature,
timestamp=args.timestamp,
nonce_str=args.nonce_str,
)
print("========== 查询订单 ==========")
print(f" 商户号: {args.mchid}")
print(f" 订单号: {args.out_trade_no}")
print("================================\n")
req = Request(full_url, method="GET")
req.add_header("Accept", "application/json")
req.add_header("Authorization", authorization)
req.add_header("Wechatpay-Serial", args.wechat_pay_public_key_id)
try:
with urlopen(req) as resp:
status_code = resp.status
resp_body = resp.read().decode("utf-8")
except HTTPError as e:
status_code = e.code
resp_body = e.read().decode("utf-8")
except URLError as e:
print(f"错误: 网络请求失败: {e.reason}", file=sys.stderr)
sys.exit(1)
print(f"========== 响应结果 ==========")
print(f"HTTP 状态码: {status_code}")
print("响应内容:")
try:
data = json.loads(resp_body)
print(json.dumps(data, indent=2, ensure_ascii=False))
except json.JSONDecodeError:
print(resp_body)
print("===============================")
sys.exit(1)
print("===============================")
if 200 <= status_code < 300:
print_order_analysis(data)
else:
print("\n---------- 请求失败 ----------")
print(f" 错误码: {data.get('code', '-')}")
print(f" 错误信息: {data.get('message', '-')}")
if data.get("code") == "ORDER_NOT_EXIST":
print(" → 订单不存在,请确认:")
print(" 1. 订单号是否正确(注意大小写和空格)")
print(" 2. 商户号是否与下单时一致")
print(" 3. 如果是合单订单,需使用合单查单接口")
print("-------------------------------")
if __name__ == "__main__":
main()