Pergunta

Estou tentando implementar a funcionalidade do PayPal IPN. O protocolo básico é como tal:

  1. O cliente é redirecionado do meu site para o site do PayPal para concluir o pagamento. Ele faz login em sua conta, autoriza o pagamento.
  2. O PayPal liga para uma página no meu servidor que passa em detalhes como postagem. Os detalhes incluem o nome, endereço e informações de pagamento de uma pessoa etc.
  3. Preciso ligar para um URL no site do PayPal internamente da minha página de processamento, passando de volta todos os parâmetros que foram aprovados no ABOVEM e um adicional chamado 'CMD' com um valor de '_notify-validate'.

Quando eu tento urllib.urlencode os parâmetros que o PayPal me enviou, eu recebo um:

While calling send_response_to_paypal. Traceback (most recent call last):
  File "<snip>/account/paypal/views.py", line 108, in process_paypal_ipn
    verify_result = send_response_to_paypal(params)
  File "<snip>/account/paypal/views.py", line 41, in send_response_to_paypal
    params = urllib.urlencode(params)
  File "/usr/local/lib/python2.6/urllib.py", line 1261, in urlencode
    v = quote_plus(str(v))
UnicodeEncodeError: 'ascii' codec can't encode character u'\ufffd' in position 9: ordinal not in range(128)

Entendo que o Urlencode faz a codificação ASCII e, em certos casos, as informações de contato de um usuário podem conter caracteres não-ASCII. Isto é incompreensível. Minha pergunta é: como codificar caracteres não-ASCII para postar em um URL usando urllib2.urlopen (req) (ou outro método)

Detalhes:

Eu li os parâmetros na solicitação original do PayPal da seguinte forma (o GET é para teste):

def read_ipn_params(request):
    if request.POST:  
        params= request.POST.copy()  
        if "ipn_auth" in request.GET:
            params["ipn_auth"]=request.GET["ipn_auth"]
        return params
    else:  
        return request.GET.copy()  

O código que eu uso para enviar de volta a solicitação ao PayPal da página de processamento é:

def send_response_to_paypal(params):
    params['cmd']='_notify-validate'  
    params = urllib.urlencode(params)  
    req = urllib2.Request(PAYPAL_API_WEBSITE, params)  
    req.add_header("Content-type", "application/x-www-form-urlencoded") 
    response = urllib2.urlopen(req)  
    status = response.read()  
    if not status == "VERIFIED":  
        logging.warn("PayPal cannot verify IPN responses: " + status)
        return False

    return True

Obviamente, o problema só surge se o nome ou endereço de alguém ou outro campo usado para o pagamento do PayPal não cair na faixa ASCII.

Foi útil?

Solução

Tente converter o dicionário de parâmetros em UTF-8 primeiro ... Urlencode parece assim melhor do que Unicode:

params = urllib.urlencode(dict([k, v.encode('utf-8')] for k, v in params.items()))

Obviamente, isso pressupõe que sua entrada seja unicode. Se sua entrada for algo diferente do Unicode, você desejará decodificá -lo para unicode primeiro e depois codificá -lo:

params['foo'] = my_raw_input.decode('iso-8859-1')
params = urllib.urlencode(dict([k, v.encode('utf-8')] for k, v in params.items()))

Outras dicas

Em vez de codificar para utf-8, deve -se codificar ao que o PayPal está usando para a postagem. Ele está disponível em Key 'Charset' no formulário PayPal envia.

Então, o código a seguir funcionou para mim:

data = dict([(k, v.encode(data['charset'])) for k, v in data.items()])

Eu sei que é um pouco tarde para o Chime aqui, mas a melhor solução que encontrei foi nem sequer analisar o que eles estavam retribuindo. Em Django (não sei o que você está usando), consegui receber a solicitação bruta que eles enviaram, que eu passei de volta literalmente. Então foi apenas uma questão de colocar a chave do CMD nisso.

Dessa forma, nunca importa o que codificando que eles enviam, você está apenas enviando de volta.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top