Интерфейс Python для PayPal - urllib.urlencode не-ASCII-символы с ошибкой
Я пытаюсь реализовать функциональность IPN PayPal. Основной протокол таков:
- Клиент перенаправляется с моего сайта на сайт PayPal для завершения платежа. Он входит в свой аккаунт, разрешает оплату.
- PayPal вызывает страницу на моем сервере, подробно переданную как POST. Подробности включают имя человека, адрес и информацию о платеже и т.д.
- Мне нужно вызвать URL-адрес на веб-сайте PayPal из моей страницы обработки, передав все параметры, которые были переданы в abovem, и еще один, называемый "cmd" со значением "_notify-validate".
Когда я пытаюсь выполнить urllib.urlencode параметры, которые PayPal отправил мне, я получаю:
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. Это понятно. Мой вопрос заключается в том, как кодировать символы, отличные от ASCII, для POSTing для URL-адреса, используя urllib2.urlopen(req) (или другой метод)
Детали:
Я прочитал параметры в исходном запросе 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.
Ответы
Ответ 1
Попробуйте сначала преобразовать словарь params в utf-8... urlencode кажется ему лучше, чем unicode:
params = urllib.urlencode(dict([k, v.encode('utf-8')] for k, v in params.items()))
Конечно, это предполагает, что ваш ввод является unicode. Если ваш ввод - это нечто иное, чем unicode, вы хотите сначала его декодировать в unicode, а затем закодировать его:
params['foo'] = my_raw_input.decode('iso-8859-1')
params = urllib.urlencode(dict([k, v.encode('utf-8')] for k, v in params.items()))
Ответ 2
Вместо кодирования на utf-8
необходимо закодировать то, что когда-либо использует paypal для сообщения.
Он доступен под ключом "charset" в форме, которую посылает paypal.
Итак, для меня работал следующий код:
data = dict([(k, v.encode(data['charset'])) for k, v in data.items()])
Ответ 3
Я знаю, что это немного поздно, чтобы прослушивать здесь, но лучшим решением, которое я нашел, было не просто разобрать то, что они возвращали. В django (не знаю, что вы используете) я смог получить необработанный запрос, который они отправили, который я передал дословно. Тогда это было просто вопрос ввода CMD-ключа.
Таким образом, никогда не имеет значения, какую кодировку они отправляют вам, вы просто отправляете его обратно.