Para implementar la subida de imágenes a WeChat Pay y obtener el MediaID en Python, se puede crear un módulo dedicado que maneje las solicitudes API y la generación de firmas.
A continuación, se presenta un ejemplo de código con una estructura modificada y nombres de variables ajustados para mayor claridad, manteniendo la funcionalidad original.
# Bibliotecas estándar
import json
import random
import hashlib
import time
import os
# Bibliotecas de terceros
import requests
from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5 as signature_pkcs_v1_5
import base64
class WeChatMediaUploader:
"""Clase para manejar la subida de medios a WeChat Pay"""
def __init__(self):
self.service_appid = "tu_appid" # AppID del servicio proveedor
self.service_mch_id = "tu_merchant_id_servicio" # ID de comerciante del servicio
self.sub_merchant_id = "tu_id_comerciante_secundario" # ID de comerciante secundario
self.callback_url = "https://www.ejemplo.com" # URL de notificación
self.merchant_id = "id_comerciante_servicio" # ID de comerciante del servicio
self.certificate_serial = "xxxxxxxxxxxxx" # Número de serie del certificado
def upload_media_image(self, image_data, file_name, file_hash):
"""
Sube una imagen y devuelve el media_id.
Documentación oficial: https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter2_1_1.shtml
:param image_data: Contenido binario de la imagen
:param file_name: Nombre del archivo con extensión válida (JPG, BMP, PNG)
:param file_hash: Hash SHA256 del archivo
:return: Respuesta JSON con media_id
"""
api_url = "https://api.mch.weixin.qq.com/v3/merchant/media/upload"
timestamp = str(int(time.time()))
nonce = self.generate_nonce()
metadata = json.dumps({"filename": file_name, "sha256": file_hash})
# Construcción del cuerpo multipart
boundary = "----WeChatBoundary"
parts = []
parts.append(f"--{boundary}".encode())
parts.append(b'Content-Disposition: form-data; name="meta"')
parts.append(b'Content-Type: application/json')
parts.append(b'')
parts.append(metadata.encode())
parts.append(f"--{boundary}".encode())
parts.append(f'Content-Disposition: form-data; name="file"; filename="{file_name}"'.encode())
parts.append(b'Content-Type: image/jpeg')
parts.append(b'')
parts.append(image_data)
parts.append(f"--{boundary}--".encode())
body = b'\r\n'.join(parts)
# Firma de la solicitud
sign_string = f"POST\n/v3/merchant/media/upload\n{timestamp}\n{nonce}\n{metadata}\n"
signature = self.generate_rsa_signature(sign_string)
auth_header = f'WECHATPAY2-SHA256-RSA2048 mchid="{self.merchant_id}",nonce_str="{nonce}",signature="{signature}",timestamp="{timestamp}",serial_no="{self.certificate_serial}"'
headers = {
'Authorization': auth_header,
'Content-Type': f'multipart/form-data; boundary={boundary}'
}
response = requests.post(api_url, data=body, headers=headers)
return response.json()
def compute_sha256(self, binary_data):
"""Calcula el hash SHA256 de datos binarios"""
hash_obj = hashlib.sha256()
hash_obj.update(binary_data)
return hash_obj.hexdigest()
def generate_nonce(self):
"""Genera una cadena aleatoria de 32 caracteres"""
characters = 'abcdefghijklmnopqrstuvwxyz0123456789'
nonce_list = [random.choice(characters) for _ in range(32)]
return ''.join(nonce_list).upper()
def generate_rsa_signature(self, data_to_sign):
"""Firma datos usando RSA con la clave privada del comerciante"""
base_path = os.path.dirname(os.path.abspath(__file__))
private_key = RSA.importKey(open(f'{base_path}/clave_privada.pem').read())
signer = signature_pkcs_v1_5.new(private_key)
hash_obj = SHA256.new(data_to_sign.encode('utf-8'))
signature_bytes = signer.sign(hash_obj)
return base64.b64encode(signature_bytes).decode('utf-8')
# Instancia del uploader
uploader_instance = WeChatMediaUploader()
Para integrar esta funcionalidad en una aplicación, se puede seguir esta lógica simplificada:
# Ejemplo de uso en una aplicación web
from modulo_wechat import uploader_instance
def process_and_upload_image(image_file):
# Leer el archivo subido
image_content = image_file.read()
file_name = image_file.name
# Calcular hash y subir imagen
content_hash = uploader_instance.compute_sha256(image_content)
response = uploader_instance.upload_media_image(image_content, file_name, content_hash)
# Extraer media_id de la respuesta
if 'media_id' in response:
return response['media_id']
else:
raise Exception(f"Error en la subida: {response}")
Este enfoque garantiza que el proceso de firma cumpla con los requisitos de WeChat Pay, evitando errores comunes relacionados con el formato del cuerpo de la solicitud.