Package: pyQiwiP2P
pyQiwiP2PПоддержка прекращенаЕсть более качественный инструмент, полностью покрывающий функции pyQiwiP2P:glQiwiApiВозможно, здесь когда-нибудь будет гайд по миграции на него, но всё в руках сообществаО библиотекеШтучка для удобной работы с кивишной апишкой платежейЕсть типадокументация, но в ней есть и косячки, поэтому,
если найдёте таковой, обязательно сообщите мне. Буду искренне рад. Правда. Спасибо.Миграция с первой версии:СвойствоBill.actualбыло удалено из-за PEP8QiwiNotifyпо умолчанию выполняет только функцию по первому подошедшему хендлеру⚠️ Важное уведомление [2023.04.05]Выпуск ключей для приёма P2P платежей через сайт официально закрыт,
но метод, заявленный вдокументации QIWI,
продолжает работать, хоть и с несколько изменённой авторизацией.Инструкция:Переходим наhttps://qiwi.com/p2p-adminАвторизуемся в свой Qiwi аккаунтОткрываем консоль браузера.Как?Вставляем код из первого блока и нажимаем enterВставляем код из второго блока и нажимаем enterЕсли прошло успешно:появится строка "Private Key: "копируем ключ, и используем по назначениюЕсли что-то пошло не так:смириться, что накосячилименя не тревожитьмне в личку не писатькусаюсь и кидаю в чсfunctioncreateKeys(name,notification_url){letp2pApiData=JSON.parse(localStorage.getItem("p2p-admin-checkout-oauth-token-head"))lettoken=btoa(p2pApiData["client_id"]+":"+p2pApiData["access_token"]).replaceAll("=","")letbody={keysPairName:name}if(notification_url){body.serverNotificationsUrl=notification_url}fetch('https://edge.qiwi.com/widgets-api/api/p2p/protected/keys/create',{method:'POST',headers:{'Accept':'application/json','Content-Type':'application/json','Authorization':"TokenHeadV2 "+token},body:JSON.stringify(body),credentials:'include',}).then(response=>{console.log(response.status);returnresponse.json();}).then(data=>{console.log(data);console.log("Private Key: "+data.result.secretKey)}).catch(error=>console.error(error));}createKeys("Tokens by WhiteApfel")// orcreateKeys("Tokens by WhiteApfel","https://qiwi.example.com/any")⚠️ Важное уведомление [2021.06.13]С июня Qiwi начала блокировать кошельки, если пользователь открыл
страницу оплаты "напрямую", тем самым не передав заголовок referer.Это случается при открытии ссылки:из мессенджераиз смсиз письмаиз адресной строкииз браузера с повышенным режимом приватности или расширениями для приватного просмотраДля обхода всех проблем, кроме последней (она не решается), к объектуBillбыл добавлен атрибутBill.alt_url, который предоставляет ссылку
для перенаправления на страницу оплаты через специальную
страницу-прокладу, добавляющую этот самый referer.Страница предоставлена мною, но её можно поднять на своём сервере
с помощью docker-контейнера.Исходники:GithubОбраз контейнера:ghcr.io/whiteapfel/pyqiwip2p:p2proxyЗапуск:docker run -p 3600:3600 -e QP2P_DOMAIN='example.com' -d ghcr.io/whiteapfel/pyqiwip2p:p2proxyСвой домен в клиенте:p2p = AioQiwiP2P(PrivKey, alt="example.com")А как пользоватьсяЧто есть?Есть сам класс QiwiP2P, который обладает тремя инструментами:
для выставления, проверки и закрытия платежа (счёта).Пример использования:frompyqiwip2pimportQiwiP2Pfrompyqiwip2p.p2p_typesimportQiwiCustomer,QiwiDatetime,PaymentMethodsQIWI_PRIV_KEY="abCdef...xYz"p2p=QiwiP2P(auth_key=QIWI_PRIV_KEY)# Выставим счет на сумму 228 рублей который будет работать 45 минутnew_bill=p2p.bill(bill_id=212332030,amount=228,lifetime=45)print(new_bill.bill_id,new_bill.pay_url)# Проверим статус выставленного счетаprint(p2p.check(bill_id=new_bill.bill_id).status)# Потеряли ссылку на оплату счета? Не проблема!print(p2p.check(bill_id=245532).pay_url)# Клиент отменил заказ? Тогда и счет надо закрытьp2p.reject(bill_id=new_bill.bill_id)# Если планируете выставлять счета с одинаковой суммой,# можно воспользоваться параметром default_amountp2p=QiwiP2P(auth_key=QIWI_PRIV_KEY,default_amount=148)# Теперь, если не указывать в методе p2p.bill() значение суммы заказа,# будет применяться указанная базовая суммаnew_bill=p2p.bill(bill_id=6627358)# А ещё можно не указывать bill_id, тогда значение сгенерируется автоматически.# Его можно будет посмотреть в объекте ответа Bill# В комбинации со стандартным значением суммы будет вот такnew_bill=p2p.bill()print(new_bill.bill_id,new_bill.pay_url)# Чтобы запретить приём платежей через какой-то метод оплаты, например, карты,# необходимо передать paySourcesFilter в fields. Туда же можно передать themeCodefields={"paySourcesFilter":"qw,card","themeCode":"MalchikGay",}p2p.bill(fields=fields)# Либо же воспользоваться удобными полямиp2p.bill(pay_sources=[PaymentMethods.qiwi,PaymentMethods.card])p2p.bill(pay_sources=[PaymentMethods.qiwi],theme_code="MalchikGay")А асинхронно могёте?Могём. Причём примерно так же.frompyqiwip2pimportAioQiwiP2Pfrompyqiwip2p.p2p_typesimportQiwiCustomer,QiwiDatetime,PaymentMethodsQIWI_PRIV_KEY="abCdef...xYz"p2p=AioQiwiP2P(auth_key=QIWI_PRIV_KEY)# Если планируете выставлять счета с одинаковой суммой,# можно воспользоваться параметром default_amountp2p=AioQiwiP2P(auth_key=QIWI_PRIV_KEY,default_amount=148)asyncdefmain():asyncwithp2p:# Выставим счет на сумму 228 рублей который будет работать 45 минутnew_bill=awaitp2p.bill(bill_id=212332030,amount=228,lifetime=45)print(new_bill.bill_id,new_bill.pay_url)# Проверим статус выставленного счетаprint((awaitp2p.check(bill_id=new_bill.bill_id)).status)# Потеряли ссылку на оплату счета? Не проблема!print((awaitp2p.check(bill_id=245532)).pay_url)# Клиент отменил заказ? Тогда и счет надо закрытьawaitp2p.reject(bill_id=new_bill.bill_id)# Если не указывать в методе p2p.bill() значение суммы заказа,# будет применяться указанная базовая сумма, которую вы установилиnew_bill=awaitp2p.bill(bill_id=6627358)# А ещё можно не указывать bill_id, тогда значение сгенерируется автоматически.# Его можно будет посмотреть в объекте ответа Bill# В комбинации со стандартным значением суммы будет вот такnew_bill=awaitp2p.bill()print(new_bill.bill_id,new_bill.pay_url)# Чтобы запретить приём платежей через какой-то метод оплаты, например, карты,# необходимо передать paySourcesFilter в fields. Туда же можно передать themeCodefields={"paySourcesFilter":"qw,card","themeCode":"MalchikGay",}awaitp2p.bill(fields=fields)# Либо же воспользоваться удобными полямиawaitp2p.bill(pay_sources=[PaymentMethods.qiwi,PaymentMethods.card])awaitp2p.bill(pay_sources=[PaymentMethods.qiwi],theme_code="MalchikGay")p2p.run(main())# Аналог# asyncio.run(main())И всё?Нет, не всё. Ещё можно настроить кивишные уведомления на свой сервер,
для этого придется немного пострадать, но лишь немного.Внимание!Для работы будет необходим проксирующий сервер (например, Nginx) с доверенным SSL-сертификатом.
Да. Подробнее про требования к проксирующему серверу вдокументации QiwiА эта шайтан-машина нужна для захендлирования функций на события. Она не готова самостоятельно контактировать с внешним миром. Пожалей её.Запросы сервер по умолчанию принимает на 8099 порту, его можно изменить, и только на/qiwi_notify- изменить нельзя.frompyqiwip2p.notifyimportQiwiNotifyfrompyqiwip2p.p2p_typesimportBillQIWI_PRIV_KEY="abCdef...xYz"qiwi_notify=QiwiNotify(QIWI_PRIV_KEY)## Хэндлер принимает в себя аргументом функцию,# в которую передаст объект счёта - Bill## Добавим хэндлер, который будет печатать billID для всех счетов@qiwi_notify.handler(lambdabill:True)defprint_bill(bill:Bill):print(bill.bill_id)# Создадим хэндлер, который будет печатать сумму оплаченных счетов@qiwi_notify.handler(lambdabill:bill.status=="PAID")defprint_bill(bill:Bill):print(bill.amount)# Теперь запустим сервер на 12345'ом портуqiwi_notify.start(port=12345)И асинхронный сервер, наверное, у вас есть?Да есть. Причём хендлить функции можно и асинхронные, и синхронные.frompyqiwip2pimportQiwiP2P,AioQiwiP2Pfrompyqiwip2p.p2p_typesimportBillfrompyqiwip2p.notifyimportAioQiwiNotifyimportasyncioQIWI_PRIV_KEY="abCdef...xYz"qiwi_notify=AioQiwiNotify(QIWI_PRIV_KEY)p2p=AioQiwiP2P(auth_key=QIWI_PRIV_KEY)@qiwi_notify.handler(lambdabill:bill.status=="EXPIRED")asyncdefon_expired(bill:Bill):new_bill=awaitp2p.bill(amount=bill.amount,comment=bill.comment)print(new_bill.pay_url)@qiwi_notify.handler(lambdabill:True)defon_all(bill:Bill):print(bill.status)asyncdefmain():p=asyncio.get_event_loop()server=p.create_task(qiwi_notify.a_start(port=12345))awaitserverloop=asyncio.get_event_loop()loop.run_until_complete(main())Настройка проксирующего NginxДля порта 12345 (как в примерах выше) будет:server {
    listen 443;
    server_name qiwinotify.domain.com;
    ssl_certificate      cert.crt;
    ssl_certificate_key  pkey.key;
    location /superSecretQiwiURI {
        proxy_pass http://0.0.0.0:12345/qiwi_notify;
    }
}В таком случае при генерации ключей API наhttps://qiwi.com/p2p-admin/transfers/apiнужно будет указатьhttps://qiwinotify.domain.com/superSecretQiwiURIв качестве URL для уведомленийP.S. за неприходящие от Qiwi запросы ответственность не несу, как и за приходящие, кстати, тоже.
Если запроса от Qiwi не было, то пишите им в поддержку@qiwi_api_help_bot