Subir imágenes de WeChat con Python para obtener MediaID

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.

Etiquetas: Python wechat-pay media-upload api-integration rsa-signing

Publicado el 6-21 05:01