20

我正在尝试实现 PayPal IPN 功能。基本协议如下:

  1. 客户从我的网站重定向到 PayPal 的网站以完成付款。他登录他的帐户,授权付款。
  2. PayPal 在我的服务器上调用一个页面,将详细信息作为 POST 传递。详细信息包括一个人的姓名、地址和付款信息等。
  3. 我需要从我的处理页面内部调用 PayPal 网站上的一个 URL,传回上面传递的所有参数,以及一个名为 'cmd' 的附加参数,其值为 '_notify-validate'。

当我尝试对 PayPal 发送给我的参数进行 urllib.urlencode 编码时,我得到:

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)

我了解 urlencode 进行 ASCII 编码,在某些情况下,用户的联系信息可以包含非 ASCII 字符。这是可以理解的。我的问题是,如何使用 urllib2.urlopen(req) (或其他方法)对非 ASCII 字符进行编码以发布到 URL

细节:

我阅读了 PayPal 原始请求中的参数如下(GET 用于测试):

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()  

我用于从处理页面将请求发送回 PayPal 的代码是:

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

显然,只有当某人的姓名或地址或用于 PayPal 支付的其他字段不属于 ASCII 范围时,才会出现问题。

4

3 回答 3

41

尝试先将 params 字典转换为 utf-8 ... urlencode 似乎比 unicode 更喜欢这样:

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

当然,这假设您的输入是 unicode。如果您的输入不是 unicode,您需要先将其解码为 un​​icode,然后对其进行编码:

params['foo'] = my_raw_input.decode('iso-8859-1')
params = urllib.urlencode(dict([k, v.encode('utf-8')] for k, v in params.items()))
于 2009-04-25T01:45:55.883 回答
6

与其编码为utf-8,不如将其编码为贝宝用于帖子的任何内容。它在贝宝发送的表单中的“字符集”键下可用。

所以下面的代码对我有用:

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

于 2010-02-08T09:03:21.390 回答
3

我知道在这里插话有点晚了,但我发现最好的解决方案是甚至不解析他们回馈的内容。在 django(不知道您在使用什么)中,我能够获得他们发送的原始请求,我将其逐字传回。然后只需将 cmd 键放在上面即可。

这样,他们发送给您的编码无关紧要,您只需将其发送回来。

于 2010-07-19T16:34:28.787 回答