Add WeChat Pay local skills

This commit is contained in:
2026-05-15 03:35:30 +08:00
parent 2eded08bc7
commit 6672867c6f
535 changed files with 114971 additions and 0 deletions

View File

@@ -0,0 +1,180 @@
#!/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()

View File

@@ -0,0 +1,179 @@
#!/usr/bin/env python3
"""
查询单笔退款(通过商户退款单号)
API文档https://pay.weixin.qq.com/doc/v3/merchant/4012791948
依赖Python3macOS/Linux 自带)
无需 pip install 任何第三方库。
所有参数通过命令行传入,脚本内不硬编码任何配置。
模型负责交互式收集参数,拼装命令行调用本脚本。
签名模式用户在自己的服务器上完成签名后将签名值Base64、时间戳、随机串传入
脚本直接使用这些值构造 Authorization 头并发送请求。
用法:
python3 查询退款.py \
--mchid <商户号> \
--serial-no <API证书序列号> \
--signature <Base64签名值> \
--timestamp <签名时使用的时间戳> \
--nonce-str <签名时使用的随机串> \
--wechat-pay-public-key-id <微信支付公钥ID> \
--out-refund-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_refund_analysis(data):
"""解析退款单关键信息。"""
print("\n========== 退款单关键信息 ==========")
print(f" 微信退款单号: {data.get('refund_id', '-')}")
print(f" 商户退款单号: {data.get('out_refund_no', '-')}")
print(f" 微信订单号: {data.get('transaction_id', '-')}")
print(f" 商户订单号: {data.get('out_trade_no', '-')}")
print(f" 退款状态: {data.get('status', '-')}")
print(f" 退款渠道: {data.get('channel', '-')}")
print(f" 退款入账账户: {data.get('user_received_account', '-')}")
print(f" 退款创建时间: {data.get('create_time', '-')}")
if data.get("success_time"):
print(f" 退款成功时间: {data['success_time']}")
amount = data.get("amount", {})
print(f" 退款金额(分): {amount.get('refund', '-')}")
print(f" 原订单金额(分): {amount.get('total', '-')}")
print(f" 用户退款(分): {amount.get('payer_refund', '-')}")
print(f" 资金账户: {data.get('funds_account', '-')}")
print("====================================")
status = data.get("status", "")
print("\n---------- 自动诊断 ----------")
if status == "SUCCESS":
print("✅ 退款成功,资金已退回用户。")
print(f" → 退款入账: {data.get('user_received_account', '-')}")
print(f" → 成功时间: {data.get('success_time', '-')}")
elif status == "PROCESSING":
print("⏳ 退款处理中PROCESSING")
print(" → 退款正在处理,通常 1-3 个工作日到账。")
print(" → 银行卡退款可能需要更长时间,请耐心等待。")
print(" → 可通过退款回调通知或定期查询确认最终结果。")
elif status == "ABNORMAL":
print("❌ 退款异常ABNORMAL")
print(" → 退款到银行发现用户账户异常或已注销,资金已退回商户。")
print(" → 需调用「发起异常退款」接口,提供其他退款账户信息重新退款。")
print(" → 代码示例: Java/8-订单退款/CreateAbnormalRefund.java")
elif status == "CLOSED":
print("⚠️ 退款关闭CLOSED")
print(" → 退款失败或被关闭,资金未退出。")
print(" → 如仍需退款,请使用新的 out_refund_no 重新发起退款申请。")
else:
print(f" 退款状态: {status}")
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-refund-no", required=True, help="商户退款单号")
args = parser.parse_args()
uri = f"/v3/refund/domestic/refunds/{quote(args.out_refund_no, 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_refund_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_refund_analysis(data)
else:
print("\n---------- 请求失败 ----------")
print(f" 错误码: {data.get('code', '-')}")
print(f" 错误信息: {data.get('message', '-')}")
if data.get("code") == "RESOURCE_NOT_EXISTS":
print(" → 退款单不存在,请确认:")
print(" 1. 商户退款单号是否正确")
print(" 2. 退款是否已成功发起(检查退款申请接口返回)")
print("-------------------------------")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,197 @@
#!/usr/bin/env python3
"""
查询订单(服务商模式 - 商户订单号查单)
API文档https://pay.weixin.qq.com/doc/v3/partner/4012085858
依赖Python3macOS/Linux 自带)
无需 pip install 任何第三方库。
所有参数通过命令行传入,脚本内不硬编码任何配置。
模型负责交互式收集参数,拼装命令行调用本脚本。
签名模式用户在自己的服务器上完成签名后将签名值Base64、时间戳、随机串传入
脚本直接使用这些值构造 Authorization 头并发送请求。
与商户版的区别:
- API 路径为 /v3/pay/partner/transactions/...
- 查询参数使用 sp_mchid + sub_mchid而非 mchid
- Authorization 头中的 mchid 为服务商商户号sp_mchid
- 响应中包含 sp_mchid/sub_mchid/sp_appid/sub_appid
用法:
python3 查询订单.py \
--sp-mchid <服务商商户号> \
--sub-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('sp_mchid', '-')}")
print(f" 子商户号: {data.get('sub_mchid', '-')}")
print(f" sp_appid: {data.get('sp_appid', '-')}")
print(f" sub_appid: {data.get('sub_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("--sp-mchid", required=True, help="服务商商户号")
parser.add_argument("--sub-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/partner/transactions/out-trade-no/{quote(args.out_trade_no, safe='')}"
f"?sp_mchid={quote(args.sp_mchid, safe='')}"
f"&sub_mchid={quote(args.sub_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.sp_mchid,
serial_no=args.serial_no,
signature=args.signature,
timestamp=args.timestamp,
nonce_str=args.nonce_str,
)
print("========== 查询订单 ==========")
print(f" 服务商商户号: {args.sp_mchid}")
print(f" 子商户号: {args.sub_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. sp_mchid 和 sub_mchid 是否与下单时一致")
print(" 3. 如果是合单订单,需使用合单查单接口")
elif data.get("code") == "INVALID_REQUEST":
print(" → 请检查 sub_mchid 是否正确,以及受理关系是否存在")
print("-------------------------------")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,190 @@
#!/usr/bin/env python3
"""
查询单笔退款(服务商模式 - 通过商户退款单号)
API文档https://pay.weixin.qq.com/doc/v3/partner/4012085948
依赖Python3macOS/Linux 自带)
无需 pip install 任何第三方库。
所有参数通过命令行传入,脚本内不硬编码任何配置。
模型负责交互式收集参数,拼装命令行调用本脚本。
签名模式用户在自己的服务器上完成签名后将签名值Base64、时间戳、随机串传入
脚本直接使用这些值构造 Authorization 头并发送请求。
与商户版的区别:
- 查询参数增加 sub_mchid
- Authorization 头中的 mchid 为服务商商户号sp_mchid
用法:
python3 查询退款.py \
--sp-mchid <服务商商户号> \
--sub-mchid <子商户号> \
--serial-no <API证书序列号> \
--signature <Base64签名值> \
--timestamp <签名时使用的时间戳> \
--nonce-str <签名时使用的随机串> \
--wechat-pay-public-key-id <微信支付公钥ID> \
--out-refund-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_refund_analysis(data):
"""解析退款单关键信息。"""
print("\n========== 退款单关键信息 ==========")
print(f" 微信退款单号: {data.get('refund_id', '-')}")
print(f" 商户退款单号: {data.get('out_refund_no', '-')}")
print(f" 微信订单号: {data.get('transaction_id', '-')}")
print(f" 商户订单号: {data.get('out_trade_no', '-')}")
print(f" 退款状态: {data.get('status', '-')}")
print(f" 退款渠道: {data.get('channel', '-')}")
print(f" 退款入账账户: {data.get('user_received_account', '-')}")
print(f" 退款创建时间: {data.get('create_time', '-')}")
if data.get("success_time"):
print(f" 退款成功时间: {data['success_time']}")
amount = data.get("amount", {})
print(f" 退款金额(分): {amount.get('refund', '-')}")
print(f" 原订单金额(分): {amount.get('total', '-')}")
print(f" 用户退款(分): {amount.get('payer_refund', '-')}")
print(f" 资金账户: {data.get('funds_account', '-')}")
print("====================================")
status = data.get("status", "")
print("\n---------- 自动诊断 ----------")
if status == "SUCCESS":
print("✅ 退款成功,资金已退回用户。")
print(f" → 退款入账: {data.get('user_received_account', '-')}")
print(f" → 成功时间: {data.get('success_time', '-')}")
elif status == "PROCESSING":
print("⏳ 退款处理中PROCESSING")
print(" → 退款正在处理,通常 1-3 个工作日到账。")
print(" → 银行卡退款可能需要更长时间,请耐心等待。")
print(" → 可通过退款回调通知或定期查询确认最终结果。")
elif status == "ABNORMAL":
print("❌ 退款异常ABNORMAL")
print(" → 退款到银行发现用户账户异常或已注销,资金已退回商户。")
print(" → 需调用「发起异常退款」接口,提供其他退款账户信息重新退款。")
print(" → 代码示例: Java/8-订单退款/CreateAbnormalRefund.java")
elif status == "CLOSED":
print("⚠️ 退款关闭CLOSED")
print(" → 退款失败或被关闭,资金未退出。")
print(" → 如仍需退款,请使用新的 out_refund_no 重新发起退款申请。")
else:
print(f" 退款状态: {status}")
print("-------------------------------")
def main():
parser = argparse.ArgumentParser(
description="查询单笔退款(服务商模式 - 通过商户退款单号)",
formatter_class=argparse.RawDescriptionHelpFormatter,
)
parser.add_argument("--sp-mchid", required=True, help="服务商商户号")
parser.add_argument("--sub-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-refund-no", required=True, help="商户退款单号")
args = parser.parse_args()
uri = (
f"/v3/refund/domestic/refunds/{quote(args.out_refund_no, safe='')}"
f"?sub_mchid={quote(args.sub_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.sp_mchid,
serial_no=args.serial_no,
signature=args.signature,
timestamp=args.timestamp,
nonce_str=args.nonce_str,
)
print("========== 查询退款 ==========")
print(f" 服务商商户号: {args.sp_mchid}")
print(f" 子商户号: {args.sub_mchid}")
print(f" 退款单号: {args.out_refund_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_refund_analysis(data)
else:
print("\n---------- 请求失败 ----------")
print(f" 错误码: {data.get('code', '-')}")
print(f" 错误信息: {data.get('message', '-')}")
if data.get("code") == "RESOURCE_NOT_EXISTS":
print(" → 退款单不存在,请确认:")
print(" 1. 商户退款单号是否正确")
print(" 2. 退款是否已成功发起(检查退款申请接口返回)")
print(" 3. sub_mchid 是否与退款申请时一致")
print("-------------------------------")
if __name__ == "__main__":
main()