Implementando Pagamento WeChat em IFrames com Comunicação Cross-Origin

Este artigo detalha a implementação do pagamento WeChat dentro de um iframe, abordando especificamente o desafio da comunicação cross-origin.

A documentação oficial para H5 (página web) iniciar o pagamento WeChat pode ser encontrada em: API JSAPI.

Para iniciar o fluxo de pagamento, o backend precisa gerar a assinatura de pagamneto requerida pelo WeChat. O seguinte código JavaScript demonstra como um cliente (frontend) pode solicitar essa assinatura e invocar a API de pagamento:


function solicitarAssinaturaEInvocarPagamento(dadosDoPedido) {
    // Requisição para obter os parâmetros de pagamento assinados do backend
    fetch('/wechat/createSign', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
        },
        body: JSON.stringify(dadosDoPedido),
    })
    .then(response => response.json())
    .then(data => {
        const parametrosPagamento = data.data; // Assumindo que os dados retornados vêm em 'data'

        // Verifica a disponibilidade da API WeixinJSBridge
        if (typeof WeixinJSBridge === 'undefined') {
            document.addEventListener('WeixinJSBridgeReady', criarRequisicaoPagamento, false);
        } else {
            criarRequisicaoPagamento();
        }

        function criarRequisicaoPagamento() {
            WeixinJSBridge.invoke(
                'getBrandWCPayRequest', {
                    "appId": parametrosPagamento.appid,
                    "timeStamp": parametrosPagamento.timeStamp,
                    "nonceStr": parametrosPagamento.nonce_str,
                    "package": "prepay_id=" + parametrosPagamento.prepay_id,
                    "signType": "MD5", // Ou o tipo de assinatura retornado pelo backend
                    "paySign": parametrosPagamento.sign
                },
                function (res) {
                    if (res.err_msg === "get_brand_wcpay_request:ok") {
                        // Pagamento bem-sucedido, redireciona para a página de sucesso
                        fetch('/wechat/toPaySuccessUrl')
                            .then(response => response.json())
                            .then(successData => {
                                window.location.href = successData.data + "?orderNo=" + parametrosPagamento.out_trade_no;
                            });
                    } else if (res.err_msg === "get_brand_wcpay_request:cancel") {
                        console.log("Usuário cancelou o pagamento.");
                        // Lógica para cancelamento
                    } else {
                        console.error("Falha no pagamento:", res);
                        // Lógica para falha
                        alert("Ocorreu um erro durante o pagamento.");
                    }
                }
            );
        }
    })
    .catch(error => {
        console.error("Erro ao obter assinatura de pagamento:", error);
        alert("Não foi possível iniciar o processo de pagamento.");
    });
}

// Exemplo de como chamar a função (assumindo que $scope.payOrder contém os dados necessários)
// solicitarAssinaturaEInvocarPagamento($scope.payOrder);

O algoritmo de asssinatura WeChat é crucial e pode ser consultado em: Algoritmo de Assinatura.

Comunicação Cross-Origin para Pagamento em Iframe

O principal desafio é invocar o pagamento WeChat de dentro de um iframe, o que normalmente envolve comunicação entre o frame e a janela pai devido às políticas de segurança do navegador (Same-Origin Policy).

No frame (iframe):

Use window.parent.postMessage para enviar dados para a janela pai. É recomendado especificar o origin do destinatário em vez de usar '\*'. No entanto, para simplificar este exemplo, usaremos '\*'. O conteúdo enviado deve ser serializado (e.g., usando JSON.stringify).


// Dentro do iframe, para notificar a janela pai
const dadosParaEnviar = { /* seus dados aqui */ };
window.parent.postMessage(JSON.stringify(dadosParaEnviar), 'http://seu-dominio-pai.com'); // Substitua pela URL correta do pai

Na janela pai (fora do iframe):

Utilize window.addEventListener('message', ...) para escutar as mensagens vindas de outros frames. A função de callback receberá um objeto event contendo os dados enviados.


// Na janela pai (página principal)
window.addEventListener('message', function(event) {
    // Verifique a origem da mensagem para segurança
    if (event.origin !== 'http://seu-dominio-iframe.com') {
        console.warn('Mensagem de origem não confiável ignorada:', event.origin);
        return;
    }

    try {
        const dadosRecebidos = JSON.parse(event.data);
        // Agora você pode chamar a função de pagamento WeChat com os dados recebidos
        iniciarPagamentoWeChat(dadosRecebidos);
    } catch (e) {
        console.error('Erro ao processar mensagem recebida:', e);
    }
});

// Função para iniciar o pagamento WeChat (pode ser a função solicitada anteriormente)
function iniciarPagamentoWeChat(parametrosWeChat) {
    // Lógica para invocar WeixinJSBridge.invoke com parametrosWeChat
    // Exemplo:
    if (typeof WeixinJSBridge !== 'undefined') {
        WeixinJSBridge.invoke('getBrandWCPayRequest', parametrosWeChat, function(res) {
            if (res.err_msg === 'get_brand_wcpay_request:ok') {
                console.log('Pagamento confirmado!');
                // Atualizar UI, etc.
            } else {
                console.log('Pagamento cancelado ou falhou:', res.err_msg);
            }
        });
    } else {
        console.error('WeixinJSBridge não está disponível.');
    }
}

Encapsulamento da Lógica de Pagamento WeChat

A seguir, um exemplo de função encapsulada para lidar com a invocação do pagamento WeChat. Esta função verifica a disponibilidade do WeixinJSBridge e o invoca:


function executarPagamentoWeChat(parametros) {
    const handler = function() {
        WeixinJSBridge.invoke(
            'getBrandWCPayRequest',
            parametros,
            function(res) {
                // O resultado da invocação
                if (res.err_msg === "get_brand_wcpay_request:ok") {
                    // Resposta "ok" não é garantia absoluta de sucesso; verifique com backend se necessário.
                    console.log("Pagamento concluído com sucesso (frontend).");
                    alert("Pagamento realizado com sucesso!");
                    // Redirecionar ou atualizar UI
                } else if (res.err_msg === "get_brand_wcpay_request:cancel") {
                    console.log("Usuário cancelou o pagamento.");
                    alert("Pagamento cancelado pelo usuário.");
                    // Considerar redirecionar ou atualizar UI
                } else {
                    console.error("Erro no pagamento:", res.err_msg);
                    alert("Falha no pagamento. Verifique os detalhes.");
                }
            }
        );
    };

    // Verifica se WeixinJSBridge já está carregado
    if (typeof WeixinJSBridge === "undefined") {
        // Se não estiver, adiciona um listener para o evento 'WeixinJSBridgeReady'
        if (document.addEventListener) {
            document.addEventListener('WeixinJSBridgeReady', handler, false);
        } else if (document.attachEvent) {
            // Para compatibilidade com IE mais antigo
            document.attachEvent('WeixinJSBridgeReady', handler);
            document.attachEvent('onWeixinJSBridgeReady', handler);
        }
    } else {
        // Se já estiver carregado, chama o handler diretamente
        handler();
    }
}

Observação Importante: Tentar acessar window.WeixinJSBridge diretamente de um iframe geralmente resultará em undefined. A comunicação via postMessage é a abordagem correta para orquestrar a chamada de pagamento a partir do frame para a janela pai, que então pode interagir com o WeixinJSBridge.

Comunicação entre Janelas (postMessage)

O método window.postMessage(message, targetOrigin) é a API padrão para comunicação cross-origin segura entre janelas (incluindo iframes).

  • message: Os dados a serem enviados (string, objeto serializado).
  • targetOrigin: Especifica quais origens têm permissão para receber a mensagem. Use um URL específico (e.g., 'https://seu-site.com') em vez de '*' para maior segurança.

Recursos adicionais sobre iframe e comunicação entre janelas:

Tags: WeChat Pay javascript iframe Cross-Origin postMessage

Publicado em 6-29 04:58