支付宝商户生成订单收款二维码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
import hashlib
import hmac
import json
import time
import requests
from urllib.parse import urlencode, unquote
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA
import base64
import qrcode
from PIL import Image
import os

class AlipayPreorderClient:
def __init__(self, app_id, merchant_private_key, alipay_public_key):
"""初始化支付宝预收单客户端"""
self.app_id = app_id
self.merchant_private_key = merchant_private_key
self.alipay_public_key = alipay_public_key
self.gateway = "https://openapi.alipay.com/gateway.do"
self.format = "JSON"
self.charset = "utf-8"
self.sign_type = "RSA2"

def generate_sign(self, params, private_key):
"""生成RSA2签名"""
params_to_sign = {k: v for k, v in params.items() if k != "sign" and v is not None}
sorted_params = sorted(params_to_sign.items(), key=lambda x: x[0])
string_to_sign = "&".join([f"{k}={v}" for k, v in sorted_params])

private_key = private_key.replace("-----BEGIN PRIVATE KEY-----", "").replace("-----END PRIVATE KEY-----", "").replace("\n", "")
private_key_bytes = base64.b64decode(private_key)
key = RSA.import_key(private_key_bytes)

h = SHA256.new(string_to_sign.encode('utf-8'))
signer = PKCS1_v1_5.new(key)
signature = signer.sign(h)
return base64.b64encode(signature).decode('utf-8')

def create_preorder(self, out_trade_no, total_amount, subject):
"""创建预收单并获取二维码链接"""
common_params = {
"app_id": self.app_id,
"method": "alipay.trade.precreate",
"format": self.format,
"charset": self.charset,
"sign_type": self.sign_type,
"timestamp": time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()),
"version": "1.0",
}

biz_content = {
"out_trade_no": out_trade_no,
"total_amount": total_amount,
"subject": subject,
"timeout_express": "30m",
"product_code": "QR_CODE_OFFLINE"
}

common_params["biz_content"] = json.dumps(biz_content, ensure_ascii=False)
common_params["sign"] = self.generate_sign(common_params, self.merchant_private_key)

url = f"{self.gateway}?charset={self.charset}"
response = requests.post(url, data=common_params)
result = response.json()

if 'alipay_trade_precreate_response' in result and result['alipay_trade_precreate_response'].get('code') == '10000':
qr_code = result['alipay_trade_precreate_response']['qr_code']
return qr_code
else:
print(f"预收单创建失败: {result}")
return None

def generate_qr_code(qr_url, save_path="alipay_payment_code.png"):
"""生成二维码图片"""
# 解码URL(处理可能的编码)
decoded_url = unquote(qr_url)

# 生成二维码
qr = qrcode.QRCode(
version=1,
error_correction=qrcode.constants.ERROR_CORRECT_L,
box_size=10,
border=4,
)
qr.add_data(decoded_url)
qr.make(fit=True)
img = qr.make_image(fill_color="black", back_color="white")

# 保存二维码图片
img.save(save_path)
print(f"二维码已保存至: {os.path.abspath(save_path)}")
return save_path

def display_qr_code(image_path):
"""显示二维码图片"""
try:
img = Image.open(image_path)
img.show()
except Exception as e:
print(f"显示二维码失败: {e}")
print(f"请手动打开: {image_path}")

# 使用示例
if __name__ == "__main__":
# 配置参数(请替换为实际值)
APP_ID = "2021005164660117"
MERCHANT_PRIVATE_KEY = """-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQClh0WVsYQzoXfYR4eb5DTQKsjBnCcKSUplT3P+XR6YFRzjQvISxNNKJCRX3Ia/XRQwsOSzx4DfhxjwDcZFg3o52TbcoJej0aSK6tjQvJ7W5V3FmKW32mZuyq1Ni2ZOTmGQwydLWurt5PW3x1b32+Bs6CoNxiVUsVFUw+zb37+Vq5G2AfQcsfkYNR8DcXqfvlP9fuD7H2I0kwdxgCdea2WUZGgiVxwWk6IMtKnXejmYdgbZcmB4wEeGL0BTs2tMwofcvo68heLuBxr7fZFXVz1UIPC6XoaY74MLu4BvHqrudeSwSLCOkiblrTFfPglD2mhYyauAJFLgc1st2OwMgQxPAgMBAAECggEACk63HZA1TEXEwboKjsGSLFhNsgi4QaK4uEBPV8qSqiO+bcRwcIzDH33KqZH47hesty98vlkxBDpUajZhX1jFlq6ZBPoXF0NpvKDNKYplVbposXJE94LpGAMU8Zw5nayx1KLiYoXh1TiJuIz4NG0Dt6RpYfwxvOlhyCZ/LF3tfzNa0o9COSG/0XqUdH3LITwBS9mF9joo4WBjQxi5+yGh48cLuj+2kLzXNEybw/9pB0N+kABmrIfZttnDs7GxFcGaar8YUamN7a7KWRWcC4BH1r23D2ngZS9FmBm4kYKySq1EOLgs8hjj5kvwqsz83uPTQDDOdhCOOq1boYKa5XjnqQKBgQD1OSOmxUpuA6mBDj5wkGe3r+HWlnpW7UwfNDrNX4P7howplxR6BCJhOsuf4cMIJmIDjXiWO8kIf4uMFRW6mNUrZYG6DRui3UYuMqtiqVjAUdX8AvXhRdyQx8ELVw7aghkTsaFe2myK0Zmx3zGlGKT4y2L6NIShe51BZuzPd3gkcwKBgQCszYigfx02MVA3U9fKk2A81nAzXw3b0jTdGxV74PGDLtg2bvEIi0wO4NoK3N0UjDQkMuLZ7MOqwGo1gHUeM2nea3mcnryj2oKR1Y7NcRAoImAryPogxdPap+MUeccvf4XZFndKCJdmzyVw5oH3p27DC2vrxIC/roHFykt/gIjdtQKBgGVO1Na6HRS0OJvTiaIxVlgBtphTlHlDEba2ejCMbFdGb7Ni8heyi42Fn8gOmedNDCMRmCbgzh3Pq5QUxeRP7Yk/J0f40FkJd0vwrPNWqQ7TAEdb5b7KgOPjUnJf5ggJxVhogxVPwZXcH9XnMnhVZRpP1DUv6zdXfVEet2jCn4TXAoGBAJ40uo6+ch8ofNl6wIkt7qEkbqsJrqV/yXhZEI1O/65r1/XWEoURCzxf7iCd3yatyhsOH5UHaIjPCy4agp940MeavEeBKRIWvw4HaGt8xR8JgeT0ZtRUaka5CHS5nXxGE2QSZnp4bSzqH8xCreDwAL8/mLnNOVbEzPkp4X9FTYk1AoGBANy9FPHWgIDNiVZCeZUJJH7UTsplqFJyvnlW1cSIWr2uOmAxbNW8jmFyUt+qNTLhplS2U4H2nkXAskb591xWZXFlUtr8dlrDmKRCUE/d1aQJU8gpEoSFDsQ/XQec/RtN12rvCs2pk/Y71hZp+Jwz0LPtCNkqLxoG5JkAz2HwY4W
-----END PRIVATE KEY-----"""
ALIPAY_PUBLIC_KEY = """-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhDhkT2FHjZBJhiiOPI8sUfp5CLc98yFY46g8CzM/BY2rXDkznjm00Ij+rCP4X9HSibq9T0PJ9l9Fs4LX0hOpN9D/v4mdvqzbLgGuwVNAD+KR6cxAv0xn3mil9FfTyne9FUAyWj+pSxZXbsh3vKV3dXvDi0rlzbqtZG+jEetcm5+FUpeezHpCdXaPZbZ9BmsWu3jf/JxorGOkvgpy/RGp1MIY3hW+Acdkb4CqE64IQH2uaclj4ubd+DhEjTCJX02SX7WS59TXQipaDRznhc5LlIPsFwPmmFwafrDC5abkh0D5nawm6yVn6hoZSg2gpWeS1DqwVQAQWkq+XdwIJJlgMQIDAQA
-----END PUBLIC KEY-----"""

# 初始化客户端
client = AlipayPreorderClient(APP_ID, MERCHANT_PRIVATE_KEY, ALIPAY_PUBLIC_KEY)

# 创建预收单
out_trade_no = f"PREORDER_{int(time.time())}"
qr_code_url = client.create_preorder(
out_trade_no=out_trade_no,
total_amount="1.00", # 建议使用非0.01元避免风控
subject="测试商品"
)

if qr_code_url:
# 生成并显示二维码
qr_image = generate_qr_code(qr_code_url)
display_qr_code(qr_image)