ForcePilot/backend/package/yuxi/channel/extensions/alipay/crypto.py
Kris 71364ea579 feat(alipay): 新增支付宝渠道插件完整实现
实现了支付宝生活号渠道的完整功能,包括消息接收回调、发送、安全验证、去重、配对绑定、流式回复支持等完整能力。
2026-05-21 10:39:23 +08:00

83 lines
3.1 KiB
Python

import base64
from urllib.parse import quote
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
class AlipayAESCrypto:
def __init__(self, aes_key: str):
raw = base64.b64decode(aes_key + "=" * (4 - len(aes_key) % 4))
self._key = raw[:16]
def decrypt(self, encrypted: str | bytes) -> str:
from cryptography.hazmat.primitives import padding as sym_padding
data = base64.b64decode(encrypted)
iv = self._key
cipher = Cipher(algorithms.AES(self._key), modes.CBC(iv))
decryptor = cipher.decryptor()
padded = decryptor.update(data) + decryptor.finalize()
unpadder = sym_padding.PKCS7(128).unpadder()
plaintext = unpadder.update(padded) + unpadder.finalize()
return plaintext.decode("utf-8")
def encrypt(self, plaintext: str) -> str:
from cryptography.hazmat.primitives import padding as sym_padding
iv = self._key
padder = sym_padding.PKCS7(128).padder()
padded = padder.update(plaintext.encode("utf-8")) + padder.finalize()
cipher = Cipher(algorithms.AES(self._key), modes.CBC(iv))
encryptor = cipher.encryptor()
encrypted = encryptor.update(padded) + encryptor.finalize()
return base64.b64encode(encrypted).decode("utf-8")
class AlipayCrypto:
def __init__(self, app_private_key_pem: str, alipay_public_key_pem: str):
self._private_key = serialization.load_pem_private_key(
app_private_key_pem.encode("utf-8"),
password=None,
backend=default_backend(),
)
self._public_key = serialization.load_pem_public_key(
alipay_public_key_pem.encode("utf-8"),
backend=default_backend(),
)
def sign(self, params: dict) -> str:
content = self._build_sign_string(params)
signature = self._private_key.sign(
content.encode("utf-8"),
padding.PKCS1v15(),
hashes.SHA256(),
)
return base64.b64encode(signature).decode("utf-8")
def verify(self, params: dict, signature: str) -> bool:
content = self._build_sign_string(params)
try:
self._public_key.verify(
base64.b64decode(signature),
content.encode("utf-8"),
padding.PKCS1v15(),
hashes.SHA256(),
)
return True
except Exception:
return False
@staticmethod
def _build_sign_string(params: dict) -> str:
filtered = {k: v for k, v in params.items() if k not in ("sign", "sign_type") and v is not None}
sorted_items = sorted(filtered.items(), key=lambda x: x[0])
parts = []
for k, v in sorted_items:
encoded_key = quote(str(k), safe="~")
encoded_value = quote(str(v), safe="~")
parts.append(f"{encoded_key}={encoded_value}")
return "&".join(parts)