Metadata-Version: 2.4
Name: podpislon
Version: 1.1.0
Summary: SDK для работы с API сервиса электронной подписи Podpislon
Author-email: Podpislon <support@podpislon.ru>
Maintainer-email: Podpislon <support@podpislon.ru>
License: MIT
Project-URL: Homepage, https://podpislon.ru
Project-URL: Documentation, https://podpislon.ru/api-docs
Project-URL: Repository, https://github.com/podpislon/podpislon-python-sdk
Project-URL: Issues, https://github.com/podpislon/podpislon-python-sdk/issues
Keywords: podpislon,electronic signature,api,sdk,электронная подпись
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Office/Business
Classifier: Typing :: Typed
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: requests>=2.25.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0.0; extra == "dev"
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
Requires-Dist: black>=23.0.0; extra == "dev"
Requires-Dist: mypy>=1.0.0; extra == "dev"
Requires-Dist: ruff>=0.1.0; extra == "dev"
Requires-Dist: types-requests>=2.25.0; extra == "dev"
Dynamic: license-file

# Podpislon Python SDK

Официальный Python SDK для работы с API сервиса электронной подписи [Podpislon](https://podpislon.ru).

## Установка

### Через pip (рекомендуется)

```bash
pip install podpislon
```

### Из исходников

```bash
git clone https://gitlab.com/podpislon/podpislon-python-sdk.git
cd podpislon-python-sdk
pip install -e .
```

## Быстрый старт

```python
from podpislon import PodpislonSDK

# Инициализация SDK
sdk = PodpislonSDK('ваш_api_токен')

# Получить информацию о компании
info = sdk.get_info()
print(f"Компания: {info['company']['name']}")
print(f"Баланс подписаний: {info['signings']}")
```

## Получение API токена

1. Зарегистрируйтесь на [podpislon.ru](https://podpislon.ru)
2. Перейдите в раздел [Интеграции](https://podpislon.ru/lk/integrations)
3. Сгенерируйте API-ключ

## Примеры использования

### Создание документа для подписания

```python
from podpislon import PodpislonSDK

sdk = PodpislonSDK('ваш_api_токен')

# Простой документ с одним подписантом
result = sdk.create_document(
    file='/path/to/document.pdf',
    name='Иван',
    last_name='Иванов',
    phone='79001234567',
    agreement=True  # Согласие на обработку персональных данных
)
print(f"Документ создан с ID: {result['result']}")
```

### Документ с несколькими подписантами

```python
result = sdk.create_document(
    file='/path/to/document.pdf',
    # Обязательные поля первого подписанта
    name='Иван',
    last_name='Иванов',
    phone='79001234567',
    agreement=True,  # Согласие на обработку персональных данных
    # Все подписанты (включая первого)
    contacts=[
        {'name': 'Иван', 'last_name': 'Иванов', 'phone': '79001234567'},
        {'name': 'Петр', 'last_name': 'Петров', 'phone': '79999999999'}
    ],
    stroke_doc=1  # строгий порядок подписания
)
```

### Документ с файлом в base64

```python
import base64

# Конвертируем файл в base64
with open('/path/to/document.pdf', 'rb') as f:
    file_base64 = base64.b64encode(f.read()).decode('utf-8')

# Или используем утилиту SDK
file_base64 = PodpislonSDK.file_to_base64('/path/to/document.pdf')

result = sdk.create_document(
    file=file_base64,
    file_name='document.pdf',  # обязательно при base64
    name='Иван',
    last_name='Иванов',
    phone='79001234567',
    agreement=True
)
```

### Получение ссылок без отправки SMS

```python
result = sdk.create_document(
    file='/path/to/document.pdf',
    name='Иван',
    last_name='Иванов',
    phone='79001234567',
    agreement=True,
    no_sms=True  # не отправлять SMS
)

# Получаем ссылки для самостоятельной отправки клиенту
print(f"ID документов: {result['result']['ids']}")
print(f"Ссылки на подписание: {result['result']['links']}")
```

### Документ с оплатой

```python
result = sdk.create_document(
    file='/path/to/document.pdf',
    name='Иван',
    last_name='Иванов',
    phone='79001234567',
    agreement=True,
    payment={
        'pid': 2,       # ID платёжной системы
        'sum': 1500.00  # сумма
    }
)
```

### Отложенная отправка

```python
import time

# Отправить документ через час
send_time = int(time.time()) + 3600

result = sdk.create_document(
    file='/path/to/document.pdf',
    name='Иван',
    last_name='Иванов',
    phone='79001234567',
    agreement=True,
    send_date=send_time
)
```

### Документ со сроком подписания

```python
import time

# Срок подписания - через 24 часа
deadline = int(time.time()) + 86400

result = sdk.create_document(
    file='/path/to/document.pdf',
    name='Иван',
    last_name='Иванов',
    phone='79001234567',
    agreement=True,
    sign_by_time=deadline
)
```

### Документ с редиректом после подписания

```python
result = sdk.create_document(
    file='/path/to/document.pdf',
    name='Иван',
    last_name='Иванов',
    phone='79001234567',
    agreement=True,
    redirect_url='https://mysite.com/success'
)
```

### Получение списка документов

```python
# Все документы
result = sdk.get_documents()
for doc in result['data']:
    print(f"{doc['id']}: {doc['name']} - {PodpislonSDK.get_status_name(doc['status'])}")

# Документы с фильтрами
result = sdk.get_documents(
    filter={
        'status': '30',  # только подписанные
        'dates': {'>=': 1704067200}  # с определённой даты
    },
    page=1,
    expand='package'  # включить ID пакета
)

# Пагинация
print(f"Страница {result['pagination']['current_page']} из {result['pagination']['page_count']}")
print(f"Всего документов: {result['pagination']['total_count']}")
```

### Получение документа по ID

```python
doc = sdk.get_document(123)
if doc:
    print(f"Название: {doc['name']}")
    print(f"Статус: {PodpislonSDK.get_status_name(doc['status'])}")
    print(f"Дата создания: {doc['date_create']}")
```

### Скачивание файла документа

```python
# Способ 1: Напрямую в файл
success = sdk.download_file(123, '/path/to/save/document.pdf')
if success:
    print("Файл сохранен")

# Способ 2: Получить base64
result = sdk.get_file(123)
if result['status']:
    # Декодируем и сохраняем вручную
    import base64
    with open('/path/to/save/document.pdf', 'wb') as f:
        f.write(base64.b64decode(result['result']))
```

### Удаление документа

```python
result = sdk.delete_document(123)
if result['ok']:
    print("Документ удален")
else:
    print(f"Ошибка: {result['mess']}")
```

### Повторная отправка ссылки

```python
# Получаем документ с package
doc = sdk.get_document(123)
package_id = doc['package']

# Переотправляем ссылку
result = sdk.resend(package_id)
if result['ok']:
    print("Ссылка отправлена повторно")

# Или конкретному контакту
contact_sid = doc['contacts'][0]['sid']
result = sdk.resend(package_id, contact_id=contact_sid)
```

### Информация о компании

```python
info = sdk.get_info()
print(f"Компания: {info['company']['name']}")
print(f"ИНН: {info['company']['inn']}")
print(f"КПП: {info['company']['kpp']}")
print(f"Баланс подписаний: {info['signings']}")
```

### Получение платёжных систем

```python
result = sdk.get_pay_systems()
for ps in result['result']:
    print(f"{ps['id']}: {ps['name']}")
```

## Контакты (REST API v2)

> Create/update/delete доступны только API-ключу администратора компании.

### Список контактов

```python
result = sdk.get_contacts({
    "limit": 20,
    "offset": 0,
    "expand": "passport,custom_fields",
    "filter": {"phone": {"like": "999"}},
})
items = result["data"]["items"]
total = result["data"]["total"]
```

### CRUD

```python
contact = sdk.create_contact({
    "phone": "+79999999999",
    "name": "Иван",
    "last_name": "Иванов",
    "email": "user@example.com",
})
contact = sdk.get_contact(contact["id"])
contact = sdk.update_contact(contact["id"], {"email": "new@example.com"})
sdk.delete_contact(contact["id"])
```

## Кастомные поля (REST API v2)

```python
fields = sdk.get_custom_fields()
field = sdk.create_custom_field({"title": "Номер договора", "type": "string"})
field = sdk.update_custom_field(field["id"], {"title": "Номер договора (обновлённый)"})
sdk.delete_custom_field(field["id"])
```

## Статусы документов

SDK предоставляет константы для статусов документов:

```python
from podpislon import PodpislonSDK

PodpislonSDK.STATUS_DRAFT              # 10 - Черновик
PodpislonSDK.STATUS_SCHEDULED          # 12 - Запланировано
PodpislonSDK.STATUS_SENT               # 15 - Отправлен
PodpislonSDK.STATUS_VIEWED             # 20 - Просмотрен
PodpislonSDK.STATUS_PARTIALLY_SIGNED   # 25 - Частично подписан
PodpislonSDK.STATUS_SIGNED             # 30 - Подписан
PodpislonSDK.STATUS_ANNULMENT_REQUESTED # 35 - Запрошено аннулирование
PodpislonSDK.STATUS_ANNULLED           # 40 - Аннулирован

# Получить название статуса
print(PodpislonSDK.get_status_name(30))  # "Подписан"
```

## Утилиты

### Работа с base64

```python
# Файл в base64
base64_str = PodpislonSDK.file_to_base64('/path/to/file.pdf')

# Base64 в файл
success = PodpislonSDK.base64_to_file(base64_str, '/path/to/save.pdf')
```

## Обработка ошибок

SDK предоставляет типизированные исключения:

```python
from podpislon import (
    PodpislonSDK,
    PodpislonException,
    AuthenticationException,
    ValidationException,
    RateLimitException,
)

sdk = PodpislonSDK('ваш_api_токен')

try:
    result = sdk.create_document(
        file='/path/to/document.pdf',
        name='Иван',
        last_name='Иванов',
        phone='79001234567',
        agreement=True
    )
except AuthenticationException as e:
    print(f"Ошибка аутентификации: {e}")
except ValidationException as e:
    print(f"Ошибка валидации: {e}")
except RateLimitException as e:
    print(f"Превышен лимит запросов: {e}")
except PodpislonException as e:
    print(f"Ошибка API: {e}")
```

## Настройка SDK

```python
sdk = PodpislonSDK(
    api_token='ваш_api_токен',
    base_url='https://podpislon.ru',  # можно изменить для тестирования
    timeout=60  # таймаут в секундах (по умолчанию 30)
)
```

## Вебхуки

Podpislon может отправлять HTTP POST-запросы на ваш сервер при определённых событиях.
Настройка URL вебхуков производится в [личном кабинете](https://podpislon.ru/lk/integrations).

### Типы событий

- `DOCUMENT_OPENED` — документ просмотрен
- `DOCUMENT_SIGNED` — документ подписан
- `CLIENT_DATA_REQUEST_SUBMITTED` — форма персональных данных заполнена

### Пример обработки вебхука (Flask)

```python
from flask import Flask, request

app = Flask(__name__)

@app.route('/webhook', methods=['POST'])
def webhook():
    event = request.form.get('EVENT')
    signature = request.form.get('SIGNATURE')
    
    if event == 'DOCUMENT_SIGNED':
        file_id = request.form.get('FILE_ID')
        company_id = request.form.get('COMPANY_ID')
        print(f"Документ {file_id} подписан!")
        
    elif event == 'DOCUMENT_OPENED':
        file_id = request.form.get('FILE_ID')
        contact = request.form.get('CONTACT')
        print(f"Документ {file_id} просмотрен контактом {contact}")
        
    elif event == 'CLIENT_DATA_REQUEST_SUBMITTED':
        client_id = request.form.get('CLIENT_ID')
        client_name = request.form.get('CLIENT_NAME')
        client_phone = request.form.get('CLIENT_PHONE')
        print(f"Новый клиент: {client_name} ({client_phone})")
    
    return 'OK', 200
```

## Ограничения API

- Максимум **4 запроса в секунду** на один API-ключ
- При превышении лимита возвращается ошибка 429

## Требования

- Python >= 3.8
- requests >= 2.25.0

## Лицензия

MIT License. См. файл [LICENSE](LICENSE).

## Поддержка

- Документация API: https://podpislon.ru/api-docs
- Email: support@podpislon.ru
