FinoPal Python SDK
کیت توسعهی پایتون برای درگاه پرداخت اینترنتی فینوپال — نوشتهشده بر اساس مستندات رسمی فینوپال.
معماری این SDK از Zarinpal Python SDK
الگو گرفته است (یک کلاینت، منابع تنبلساز، متدهای create / verify / inquire /
reverse / calculate) اما API آن دقیقاً همان چیزی است که فینوپال ارائه میدهد و
با SDK موجود PHP در افزونهی ووکامرس همین پروژه (finopay-plugins/finopal-woocommerce)
همخوان است.
pip install finopal-py-sdk
تا زمان انتشار روی PyPI، بسته را از همین پوشه نصب کنید:
pip install .
فهرست
- نصب
- شروع سریع
- جریان کامل پرداخت
- منابع API
- حالت Sandbox
- تبدیل مبلغ (تومان ↔ ریال)
- مدیریت خطا
- کدهای خطا
- تستها
- ساختار پروژه
- English summary
نصب
نیازمندی: Python 3.9+ و pip. وابستگی زمان اجرا بستهی requests است.
pip install finopal-py-sdk
یا:
pip3 install finopal-py-sdk
تا زمان انتشار روی PyPI:
pip install .
# حالت توسعه، همراه با تستها
pip install -e ".[dev]"
برای اجرای مثال Flask:
pip install ".[flask]"
شروع سریع
from finopal import FinoPal, Config
finopal = FinoPal(Config(
merchant_id="fino-xxxx-xxxx-xxxx-xxxx", # از پنل کاربری فینوپال
sandbox=True, # برای محیط آزمایشی
))
payment = finopal.payments.create({
"amount": 1_100, # مبلغ به ریال
"callback_url": "https://shop.example.com/payment/callback",
"order_id": "ORD-1001",
"description": "خرید شماره ۱۰۰۱",
})
print(payment.authority) # FP_636E4A5CBBCE0F222B46829BD74AB4A9
print(payment.payment_url) # کاربر را به این آدرس هدایت کنید
هر پاسخ، همزمان با دسترسی دیکشنری و هم با دسترسی ویژگی قابل استفاده است و
پیلود خام گیتوی همیشه در raw نگه داشته میشود:
payment.authority # → "FP_636E..."
payment["authority"] # → "FP_636E..."
payment.raw # → {"success": True, "code": 100, "data": {...}}
payment.is_successful # → True
جریان کامل پرداخت
┌──────────┐ 1. request ┌────────────┐
│ سایت شما │ ───────────────▶│ فینوپال │
└──────────┘ authority + └────────────┘
│ payment_url │
│ 2. هدایت کاربر (302) │
└─────────────────────────────▶│ صفحه پرداخت
│
┌──────────┐ 3. callback ┌──────┴─────┐
│ سایت شما │ ◀───────────────│ فینوپال │
└──────────┘ ?Authority=.. └────────────┘
│ &Status=OK
│ 4. verify (فقط اگر Status=OK باشد)
└─────────────────────────────▶ ref_id
۱) ساخت درخواست پرداخت
payment = finopal.payments.create({
"amount": 120_000, # ریال — الزامی
"callback_url": "...", # الزامی، باید با دامنهی ثبتشده یکی باشد
"order_id": "ORD-1001", # الزامی
"description": "...", # الزامی، حداکثر ۲۵۵ کاراکتر
"mobile": "09121234567", # اختیاری
"email": "a@b.com", # اختیاری
"metadata": {"user_id": "960", "product_id": "123"},
})
redirect(payment.payment_url) # کاربر را هدایت کنید
میانبر با آرگومانهای کلیدواژهای:
payment = finopal.payments.request(
amount=120_000,
callback_url="https://shop.example.com/callback",
order_id="ORD-1001",
)
۲) بازگشت کاربر و تأیید (verify)
فینوپال کاربر را با کوئریاسترینگ برمیگرداند. قالب پارامترها در محیط واقعی و
آزمایشی متفاوت است و parse_callback هر دو را یکسان میکند:
| محیط | کوئریاسترینگ | نشانهی موفقیت |
|---|---|---|
| واقعی | ?Authority=...&Status=OK |
Status=OK |
| آزمایشی | ?authority=...&status=0&sandbox=1 |
status=0 |
from finopal import parse_callback
cb = parse_callback(request.args)
if not cb.is_success:
return render_template("payment_failed.html")
result = finopal.verifications.verify({
"authority": cb.authority,
"amount": order.amount_rial, # دقیقاً همان مبلغ مرحلهی اول
})
print(result.ref_id) # شماره تراکنش برای نمایش به کاربر
مهم: متد
verifyرا فقط زمانی صدا بزنید کهStatus=OK(یا در سندباکسstatus=0) باشد. کد100یعنی «تازه تأیید شد» و کد101یعنی «قبلاً تأیید شده بود» — هر دو یعنی پرداخت موفق است.
۳) استعلام (اختیاری ولی توصیهشده)
اگر verify با خطای شبکه برگشت، هرگز دوباره درخواست پرداخت نسازید؛ وضعیت
واقعی را استعلام کنید:
status = finopal.inquiries.inquire({"authority": cb.authority})
print(status.status) # pending | successful | failed | cancelled | refunded | callback
print(status.is_paid) # True فقط وقتی status == "successful"
۴) ریورس (لغو تراکنش تأییدشده)
finopal.reversals.reverse({"authority": "FP_636E4A5C..."})
- فقط روی تراکنش verifyشده و در بازهی کوتاه (۱۵ دقیقه بر اساس مستندات) کار میکند.
- بعد از موفقیت، وضعیت تراکنش
refundedمیشود و تکرار درخواست خطای-1551میدهد. - انقضای مهلت → خطای
-1552.
۵) محاسبه کارمزد (قبل از پرداخت)
fee = finopal.fees.calculate({"amount": 1_000_000})
print(fee.fee, fee.fee_type) # 20000 Merchant
print(fee.is_paid_by_customer) # False → کارمزد روی پذیرنده است
منابع API
| منبع | متدها | اندپوینت |
|---|---|---|
finopal.payments |
create() / request() / redirect_url() |
POST /api/v1/payment/request |
finopal.verifications |
verify() / verify_authority() / is_verified() |
POST /api/v1/payment/verify |
finopal.inquiries |
inquire() / status_of() |
POST /api/v1/payment/inquiry |
finopal.reversals |
reverse() / reverse_authority() |
POST /api/v1/payment/reverse |
finopal.fees |
calculate() / calculate_amount() |
POST /api/v1/payment/feeCalculation |
اندپوینت جدیدی که هنوز پوشش داده نشده است:
finopal.request("POST", "/api/v1/payment/anything", {"foo": "bar"})
حالت Sandbox
Config(merchant_id="...", sandbox=True) # → https://sandbox.finopal.ir
Config(merchant_id="...") # → https://payment.finopal.ir
Config(merchant_id="...", base_url="http://localhost:8000") # میزبان دلخواه
ساختار API در سندباکس یکسان است؛ فقط مقصد نهایی بانک شبیهسازی میشود. در صفحهی آزمایشی میتوانید نتیجه را انتخاب کنید:
| دکمه | نتیجه در Callback |
|---|---|
| پرداخت موفق | Status=0 / status=OK |
| پرداخت ناموفق | Status=-1 / status=NOK |
| انصراف | status=-138 |
مدت اعتبار نشست سندباکس ۱۰ دقیقه است.
تبدیل مبلغ (تومان ↔ ریال)
فینوپال همهی مبلغها را ریال و بهصورت عدد صحیح میپذیرد. برای جلوگیری از اشتباه ضریب ده، از این توابع استفاده کنید:
from finopal import to_rial, to_toman
to_rial(12_000, "TOMAN") # → 120000
to_rial(5, "IRHT") # → 50000 (هزار تومان)
to_rial(1_100) # → 1100 (پیشفرض ریال)
to_toman(120_000) # → 12000.0
مبالغ مجاز: حداقل 1,000 ریال و حداکثر 1,000,000,000 ریال (۱۰۰ میلیون تومان).
مدیریت خطا
from finopal import FinoPalError, FinoPalException, ValidationError, TransportError
try:
result = finopal.verifications.verify({"authority": authority, "amount": amount})
except ValidationError as exc:
# ورودی نامعتبر — هیچ درخواستی به گیتوی ارسال نشده است
...
except FinoPalException as exc:
# گیتوی پاسخ success=false یا کد غیر ۱۰۰ داده است
print(exc.code, exc.message, exc.describe(), exc.scope)
except TransportError as exc:
# خطای شبکه / پاسخ غیر JSON / خطای 5xx — نتیجه نامعلوم است
# → قبل از هر تصمیمی استعلام بگیرید
print(exc.status_code, exc.message)
| کلاس | معنا |
|---|---|
ValidationError |
اعتبارسنجی سمت کلاینت؛ درخواستی ارسال نشده است |
FinoPalException |
گیتوی درخواست را رد کرده؛ دارای code و message |
TransportError |
عدم دسترسی به گیتوی یا پاسخ نامعتبر؛ دارای status_code |
describe() توضیح انگلیسی کد خطا را از فهرست رسمی برمیگرداند و scope مشخص
میکند کد مربوط به کدام بخش API است (PaymentRequest، PaymentVerify، ...).
کدهای خطا
فهرست کامل در src/finopal/utils/error_codes.py قرار دارد. مهمترینها:
| کد | توضیح |
|---|---|
100 |
عملیات موفق |
101 |
تراکنش قبلاً تأیید شده است (پرداخت موفق است) |
-9 |
خطای اعتبارسنجی (مرچنتکد، callback، توضیحات، مبلغ) |
-10 |
مرچنتکد یا آیپی پذیرنده صحیح نیست |
-11 |
مرچنتکد فعال نیست / تراکنش یافت نشد |
-13 |
محدودیت تراکنش ترمینال |
-14 |
دامنهی callback با دامنهی ثبتشده مغایرت دارد |
-15 |
درگاه در حالت تعلیق |
-16 / -17 |
سطح تأیید پذیرنده نامعتبر / محدودیت سطح آبی |
-18 |
عدم تطابق آدرس referrer با دامنهی ثبتشده |
-19 |
ایجاد تراکنش برای این ترمینال مجاز نیست |
-30 … -41 |
خطاهای ساخت درخواست پرداخت (تسهیم، کارمزد، سقف مبلغ) |
-50 |
مبلغ وریفای با مبلغ پرداختشده یکسان نیست |
-51 |
پرداخت ناموفق / نشست فعال نیست |
-53 |
پرداخت متعلق به این مرچنتکد نیست |
-54 |
authority نامعتبر |
-55 |
تراکنش مورد نظر یافت نشد |
-60 … -63 |
خطاهای ریورس |
-1551 |
تراکنش قبلاً برگشت داده شده است |
-1552 |
تراکنش verify نشده یا مهلت ریورس تمام شده است |
تستها
pip install -e ".[dev]"
pytest
مجموعهی تست آفلاین و قطعی است: کلاس FakeGateway (در tests/conftest.py) نقش
گیتوی را بازی میکند و لایهی HTTP واقعی هم با requests_mock تست میشود.
ساختار پروژه
finopal-python-sdk/
├── src/finopal/
│ ├── __init__.py # API عمومی
│ ├── finopal.py # کلاس FinoPal (ورودی اصلی)
│ ├── models.py # پاسخهای نرمالشده (ApiResponse و ...)
│ ├── errors/exceptions.py # سلسلهمراتب خطاها
│ ├── http/
│ │ ├── transport.py # Transport، RequestsTransport، CallableTransport
│ │ └── client.py # HttpClient
│ ├── resources/ # payments, verifications, inquiries, reversals, fees
│ └── utils/ # config, validator, amount, callback, error_codes, enums
├── examples/ # ۶ مثال قابل اجرا (شامل جریان کامل Flask)
└── tests/ # pytest + FakeGateway
English summary
A Python SDK for the FinoPal online payment gateway, modelled on the ZarinPal Python SDK and consistent with the FinoPal WooCommerce plugin already in this repository.
from finopal import FinoPal, Config, parse_callback, to_rial
finopal = FinoPal(Config(merchant_id="fino-xxxx-xxxx-xxxx-xxxx", sandbox=True))
payment = finopal.payments.create({
"amount": to_rial(12_000, "TOMAN"),
"callback_url": "https://shop.example.com/callback",
"order_id": "ORD-1001",
})
# -> redirect the buyer to payment.payment_url
cb = parse_callback(request.args)
if cb.is_success:
result = finopal.verifications.verify({
"authority": cb.authority,
"amount": 120_000,
})
print(result.ref_id)
Coverage: request, verify, inquiry, reverse, feeCalculation.
Extras: Rial/Toman conversion, live+sandbox callback normalisation, the full
published error catalogue, pluggable transports, and a fully offline test suite.
مجوز
MIT — see LICENSE.
Metadata
Release files for finopal-py-sdk 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| finopal_py_sdk-0.1.0.tar.gz | 40.6 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| finopal_py_sdk-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 77.6 kB
Release files / finopal_py_sdk-0.1.0.tar.gz
| Download URL | finopal_py_sdk-0.1.0.tar.gz |
|---|---|
| Size | 40.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a818debd09e12f02086d8fccf8f80edc74e49b4116b84866a1febbfa2b3442c8
|
|
BLAKE2b-256 checksum How to use checksums |
ed270b52995a30366cb3e97c5ae8c723cf5d32fe3b3220f5c0d597dc0227dc0c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.3
|
Release files / finopal_py_sdk-0.1.0-py3-none-any.whl
| Download URL | finopal_py_sdk-0.1.0-py3-none-any.whl |
|---|---|
| Size | 37.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
f6d8e27932879c570c623244065bb3757766bc214b82c963b98cff6c51b2918e
|
|
BLAKE2b-256 checksum How to use checksums |
110f6ef0f49e25d482070f1e184c540e9907c86761c4e24f59e80fa3c4fa1f21
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.3
|
Security for finopal-py-sdk 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.