Подключение интернет-магазина к WebMoney на Python
http://habrahabr.ru/blogs/django/86123/
Организационная часть
Существует несколько вариантов подключения к системе WebMoney. В данной статье я опишу два варианта: WebMoney XML-interface (по сути WebMoney API для продавцов) и Web Merchant Interface (переход на страницу оплаты WebMoney по специально сформированной ссылке).
Чтобы иметь возможность работать с любым из этих вариантов, вам нужно получить аттестат продавца. То есть зарегистрироваться и подтвердить, что вы являетесь юридическим лицом. Правда на практике все нужные нам функции обоих вариантов подключения работают и при обычном персональном аттестате (хотя в документации и написано обратное). Поэтому смело можете начать с него и потом получить аттестат продавца. Процесс получения персонального аттестата или аттестата продавца выходит за пределы данной статьи.
Вариант подключения через WebMoney XML-interface
Для работы с ним нам потребуются библиотеки openssl и curl (скомпилированный с поддержкой ssl). Плюс потребуется питоновская библиотека для работы с curl: pycurl. Команды для инсталляции библиотек из мак-портов (в убунту ставим из пакетов):
sudo port install openssl sudo port install curl +ssl sudo port install py26-curl
Предупреждение: не используйте версию openssl 0.9.8l. В ней баг работы с ssl-ключами! К сожалению на данный момент эта библиотека идет по умолчанию в мак-портах. Я потратил несколько часов, прежде чем понял, что дело было в ней. Точно рабочими являются версии openssl 0.9.8k или 0.9.8g.
Для оплаты товара через WebMoney из всех функций API нам нужны только следующие:
— Выставление счета — Проверка, что счет оплачен
Все запросы и ответы производятся в формате XML. Не буду долго расписывать и просто приведу примеры кода.
Выставить счет на оплату:
Copy Source | Copy HTML
xml_request = """\
<w3s.request>
<reqn>%s</reqn>
<invoice>
<orderid>%s</orderid>
<customerwmid>%s</customerwmid>
<storepurse>%s</storepurse>
<amount>%s</amount>
<desc>%s</desc>
<period>0</period>
<expiration>0</expiration>
</invoice>
</w3s.request>
""" % (_get_reqn(), payment_id, customer_wmid, your_web_money_purse, payment_amount,
payment_description)
result = request('https://w3s.wmtransfer.com/asp/XMLInvoiceCert.asp',
xml_request.encode('utf-8'))
Пояснение параметров запроса:
* _get_reqn() — функция формирования уникального номера запроса (приведена ниже) * payment_id — id платежа в вашем интернет-магазине * customer_wmid — WMID покупателя (предполагается, что у покупателя есть кошелек в той же валюте, что и у вас) * your_web_money_purse — ваш номер кошелька, например: R12345678900 * payment_amount — цена вашего заказа * payment_description — описание заказа
В XML-ответе будет содержаться номер выставленного счета (invoice_id) и дата его создания (created). Мы используем их в следующем запросе.
Проверка, что счет оплачен:
Copy Source | Copy HTML
from datetime import timedelta
xml_request = """\
<w3s.request>
<reqn>%s</reqn>
<getoutinvoices>
<purse>%s</purse>
<wminvid>%s</wminvid>
<orderid>%s</orderid>
<datestart>%s 00:00:00</datestart>
<datefinish>%s 00:00:00</datefinish>
</getoutinvoices>
</w3s.request>
""" % (_get_reqn(), customer_web_money_purse, invoice_id, payment_id,
created.strftime('%Y%m%d'), (created + timedelta(1)).strftime('%Y%m%d'))
result = request('https://w3s.webmoney.ru/asp/XMLOutInvoicesCert.asp', xml_request.encode('utf-8'))
Пояснение параметров запроса:
* your_web_money_purse — ваш номер кошелька, например: R12345678900 * invoice_id — номер выставленного счета в WebMoney * payment_id — номер счета в вашем интернет-магазине * created — дата создания счета в WebMoney
В XML-ответе, будет указано состояние счета. Возможные варианты состояния:
1 — не оплачен
2 — оплачен
3 — отменен пользователем
Самое интересное — код функций формирования уникального номера и самого запроса:
Copy Source | Copy HTML
import pycurl
import random
import StringIO
from time import time, sleep
def _get_reqn():
return int(time()*100)
def request(url, xml_body, retries=5, delay=5):
import os.path
current_dir = os.path.dirname(__file__)
for i in range( 0, retries):
def is_last_attempt():
return i >= retries - 1
try:
r = pycurl.Curl()
r.setopt(pycurl.URL, url)
r.setopt(pycurl.TIMEOUT, 30)
r.setopt(pycurl.HEADER, 0)
r.setopt(pycurl.POST, 1)
b = StringIO.StringIO()
r.setopt(pycurl.WRITEFUNCTION, b.write)
r.setopt(pycurl.POSTFIELDS, xml_body)
r.setopt(pycurl.CAINFO, current_dir + 'WebMoneyCA.crt')
r.setopt(pycurl.SSLKEY, current_dir + 'your_key.key')
r.setopt(pycurl.SSLKEYPASSWD, '12345')
r.setopt(pycurl.SSLCERT, current_dir + 'your_certificate.cer')
r.perform()
return b.getvalue()
except BaseException as e:
logging.warning(u'Http request to web-money, Curl error: %s' % e) # e.args[0] - error code
if is_last_attempt():
raise
finally:
r.close()
sleep(delay)
Пояснение:
В параметрах запроса указываются следующие файлы: сертификат WebMoney (WebMoneyCA.crt), ваш ssl-сертификат (your_certificate.cer), ваш ssl-ключ (your_key.key). Так же указывается пароль к ключу: «12345». Как сформировать сертификат и ключ вы можете посмотреть в статье Программное обращение к XML-интерфейсам системы... (внизу). Сертификат WebMoney вы можете взять с сайта WebMoney
Вариант подключения через web merchant interface
Тут все проще, никаких дополнительных библиотек нам ставить не нужно. Все что нужно — это сформировать POST запрос на страницу https://merchant.webmoney.ru/lmi/payment.asp. Пример html-формы:
Copy Source | Copy HTML <form action="https://merchant.webmoney.ru/lmi/payment.asp" method="post" id="web_money_redirect_form"> <input type="hidden" name="LMI_PAYMENT_AMOUNT" value="10.0"> <input type="hidden" name="LMI_PAYMENT_DESC" value="Описание товара"> <input type="hidden" name="LMI_PAYMENT_NO" value="11"> <input type="hidden" name="LMI_PAYEE_PURSE" value="Номер вашего кошелька в Web-money"> <input type="hidden" name="LMI_SUCCESS_URL" value="URL-страницы в случае успешной операции"> <input type="hidden" name="LMI_SUCCESS_METHOD" value="1"> <input type="hidden" name="LMI_FAIL_URL" value="URL-страницы в случае ошибки проведения платежа"> <input type="hidden" name="LMI_FAIL_METHOD" value="1"> <input type="submit" value="Перейти на сайт Web Money для оплаты" id="return_button"> </form>
Описание переменных формы:
* LMI_PAYMENT_AMOUNT — стоимость вашего заказа * LMI_PAYMENT_DESC — текстовое описание заказа * LMI_PAYMENT_NO — номер платежа в вашей системе (в вашем интернет-магазине) * LMI_PAYEE_PURSE — Номер вашего кошелька в WebMoney, например R12345678900 * LMI_SUCCESS_URL — URL вашей страницы, на которую покупатель попадет после оплаты в системе WebMoney в случае успешной операции * LMI_SUCCESS_METHOD — метод вызова вашей успешной страницы, указанной в LMI_SUCCESS_URL. Варианты: 0 — GET, 1 — POST, 2 — LINK * LMI_FAIL_URL — URL вашей страницы, на которую покупатель попадет в случае ошибки оплаты в системе WebMoney или если покупатель просто отказался от оплаты * LMI_FAIL_METHOD — метод вызова вашей страницы в случае ошибки. Варианты такие же: 0 — GET, 1 — POST, 2 — LINK
Примечание: вы можете указать LMI_SUCCESS_URL, LMI_SUCCESS_METHOD, LMI_FAILED_URL и LMI_FAILED_METHOD в настройках вашего кошелька в WebMoney, если они у вас всегда одинаковые и не указывать их в параметрах формы. В нашем случае они у нас всегда меняются.
В процессе оплаты WebMoney дополнительно вызовет указанный в настройках вашего кошелька Result URL, причем два раза: сначала до снятия денег (предварительный запрос) и второй раз в случае успешного снятия денег. Если произошла ошибка в процессе оплаты, то второго вызова не будет. Метод вызова: POST.
Параметры предварительного запросы мы пропустим, тем более что они по умолчанию пустые (указывается в настройках вашего кошелька в WebMoney). Если вы не собираетесь обрабатывать предварительный запрос, то в коде django view можно написать такую заглушку:
Copy Source | Copy HTML
if not request.POST or request.POST.get('LMI_PREREQUEST', None):
return HttpResponse('OK')
Важные для нас параметры второго POST-запроса от WebMoney:
* LMI_PAYEE_PURSE — номер нашего кошелька в WebMoney * LMI_PAYMENT_AMOUNT — стоимость вашего заказа * LMI_PAYMENT_NO — номер заказа в нашем интернет-магазине * LMI_MODE — 1 — тестовый режим (реально деньги в WebMoney не снимаются), 0 — нормальный режим работы * LMI_SYS_INVS_NO — номер счета в системе WebMoney * LMI_SYS_TRANS_NO — номер транзакции в системе WebMoney * LMI_SYS_TRANS_DATE — дата транзакции * LMI_PAYER_PURSE — номер кошелька покупателя * LMI_PAYER_WM — WMID покупателя * LMI_HASH — подпись, чтобы проверить параметры запроса и убедиться, что это действительно запрос от WebMoney
В WebMoney есть два алгоритма формирования подписи: MD5 и собственный алгоритм (SIGN).В случае использования MD5 код
проверки подписи в вашем view может быть такой:
Copy Source | Copy HTML
import hashlib
string_to_sign = .join([request.POST['LMI_PAYEE_PURSE'], request.POST['LMI_PAYMENT_AMOUNT'],
request.POST['LMI_PAYMENT_NO'], request.POST['LMI_MODE'],
request.POST['LMI_SYS_INVS_NO'], request.POST['LMI_SYS_TRANS_NO'],
request.POST['LMI_SYS_TRANS_DATE'], secret_key,
request.POST['LMI_PAYER_PURSE'], request.POST['LMI_PAYER_WM']])
sign = hashlib.md5(string_to_sign).hexdigest().upper()
if sign != request.POST['LMI_HASH']:
return HttpResponseBadRequest('Some error')
Как видно из кода, вам нужно указать в переменной secret_key ваш секретный ключ, который вы указали в настройках вашего кошелька.
Так же хорошей идеей является проверить номер вашего кошелька и стоимость товара, что они действительно совпадают с тем, что вы ожидаете:
Copy Source | Copy HTML
if request.POST['LMI_PAYEE_PURSE'] != your_web_money_purse:
return HttpResponseBadRequest('Some error')
if request.POST['LMI_PAYMENT_AMOUNT'] != your_payment_amount:
return HttpResponseBadRequest('Some error')
Дальше вы можете делать, все что нужно для вашего интернет-магазина, чтобы отметить, что товар был оплачен и в самом конце view написать ответ на запрос: return HttpResponse('OK')
Заключение
Надеюсь описание было понятным и полезным. Замечания по теме статьи всегда приветствуются :)