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: