Skip to content

API Reference - Integrations

Pydantic

instapay_eg.integrations.pydantic

Pydantic v2 integration for instapay-eg.

Provides annotated types and a ready-made model that integrate seamlessly with Pydantic v2 and FastAPI.

Usage::

from pydantic import BaseModel
from instapay_eg.integrations.pydantic import InstaPayLink, InstaPayHandle

class PaymentRequest(BaseModel):
    link: InstaPayLink          # validates + extracts the URL automatically
    recipient: InstaPayHandle   # validates the handle format

FastAPI example::

from fastapi import FastAPI
from instapay_eg.integrations.pydantic import InstaPayPaymentModel

app = FastAPI()

@app.post("/payments")
async def create_payment(payment: InstaPayPaymentModel):
    return {"link": payment.link, "handle": payment.handle}

Raises:

Type Description
ImportError

If pydantic is not installed. Install with pip install "instapay-eg[pydantic]".

InstaPayLink = Annotated[
    str,
    AfterValidator(_validate_instapay_link),
    Field(
        examples=["https://ipn.eg/S/alice/instapay/2DcFGv"],
        description="An Egyptian InstaPay payment link.  Accepts raw share-sheet text from the 'InstaPay Egypt' app (the link will be extracted and security-checked automatically).",
        json_schema_extra={
            "format": "uri",
            "pattern": "^https://ipn\\.eg/",
        },
    ),
]

Annotated str type that validates, extracts, and security-checks an InstaPay payment link. Use this as a field type in any Pydantic BaseModel or FastAPI request/response schema.

InstaPayHandle module-attribute

InstaPayHandle = Annotated[
    str,
    AfterValidator(_validate_instapay_handle),
    Field(
        examples=["alice", "alice@instapay"],
        description="An InstaPay handle.  The ``@instapay`` suffix is stripped and normalised automatically.",
        json_schema_extra={
            "pattern": "^[a-zA-Z0-9]+([._\\-][a-zA-Z0-9]+)*$"
        },
    ),
]

Annotated str type that validates and normalises an InstaPay handle. Accepts both 'alice' and 'alice@instapay' - the suffix is stripped.

InstaPayPaymentModel

Bases: BaseModel

A ready-to-use Pydantic model representing an InstaPay payment request.

Attributes:

Name Type Description
link InstaPayLink

The validated InstaPay payment URL.

handle InstaPayHandle

The normalised recipient handle (without @instapay).

Example::

class MyPaymentForm(InstaPayPaymentModel):
    note: str = ""

form = MyPaymentForm(
    link="https://ipn.eg/S/alice/instapay/2DcFGv",
    handle="alice",
)
Source code in src/instapay_eg/integrations/pydantic.py
class InstaPayPaymentModel(BaseModel):
    """A ready-to-use Pydantic model representing an InstaPay payment request.

    Attributes:
        link: The validated InstaPay payment URL.
        handle: The normalised recipient handle (without ``@instapay``).

    Example::

        class MyPaymentForm(InstaPayPaymentModel):
            note: str = ""

        form = MyPaymentForm(
            link="https://ipn.eg/S/alice/instapay/2DcFGv",
            handle="alice",
        )
    """

    link: InstaPayLink
    handle: InstaPayHandle

QR Code

instapay_eg.integrations.qrcode

QR code generation integration for instapay-eg.

Generates QR codes for InstaPay payment links using the segno library. Supports PNG, SVG, and base64-encoded output - covering every common use case from saving to disk, embedding in web pages, and returning from JSON APIs.

Usage::

from instapay_eg.integrations.qrcode import qr_as_base64, save_qr

# For a JSON API response:
b64 = qr_as_base64("https://ipn.eg/S/alice/instapay/2DcFGv")

# For saving to disk:
save_qr("https://ipn.eg/S/alice/instapay/2DcFGv", "alice_payment.png")

Raises:

Type Description
ImportError

If segno is not installed. Install with pip install "instapay-eg[qrcode]".

generate_qr

generate_qr(
    link: str, *, error: str = "m"
) -> segno_module.QRCode

Generate a segno.QRCode object for link.

Parameters:

Name Type Description Default
link str

A validated https://ipn.eg payment URL.

required
error str

QR error correction level. One of 'l', 'm' (default), 'q', or 'h'. Higher levels allow more of the QR code to be obscured while remaining scannable (useful when adding a logo).

'm'

Returns:

Name Type Description
A QRCode

class:segno.QRCode instance. Call .save(), .svg_inline(),

QRCode

or any other segno method on it directly.

Example

qr = generate_qr("https://ipn.eg/S/alice/instapay/2DcFGv") qr.save("payment.png", scale=10)

Source code in src/instapay_eg/integrations/qrcode.py
def generate_qr(
    link: str,
    *,
    error: str = "m",
) -> segno_module.QRCode:  # ty:ignore[unresolved-attribute]
    """Generate a ``segno.QRCode`` object for *link*.

    Args:
        link: A validated ``https://ipn.eg`` payment URL.
        error: QR error correction level.  One of ``'l'``, ``'m'`` (default),
            ``'q'``, or ``'h'``.  Higher levels allow more of the QR code to
            be obscured while remaining scannable (useful when adding a logo).

    Returns:
        A :class:`segno.QRCode` instance.  Call ``.save()``, ``.svg_inline()``,
        or any other segno method on it directly.

    Example:
        >>> qr = generate_qr("https://ipn.eg/S/alice/instapay/2DcFGv")
        >>> qr.save("payment.png", scale=10)
    """
    segno = _get_segno()
    return segno.make(link, error=error)

save_qr

save_qr(
    link: str,
    path: str | Path,
    *,
    scale: int = 10,
    dark: str = "#1a1a2e",
    light: str = "#ffffff",
    file_format: str | None = None,
) -> None

Save a QR code image for link to path on disk.

The output format is inferred from the file extension of path (e.g. payment.png → PNG, payment.svg → SVG) unless file_format is given explicitly.

Parameters:

Name Type Description Default
link str

A validated https://ipn.eg payment URL.

required
path str | Path

Destination file path. Parent directories must already exist.

required
scale int

Pixel size of each QR module. 10 produces a ~330x330 px image at the default data density.

10
dark str

Hex colour for the dark modules (default: deep navy #1a1a2e).

'#1a1a2e'
light str

Hex colour for the light modules (default: white #ffffff).

'#ffffff'
file_format str | None

Override the output format (e.g. 'png', 'svg'). When None, the format is inferred from path.

None
Example

save_qr( ... "https://ipn.eg/S/alice/instapay/2DcFGv", ... "alice_qr.png", ... scale=12, ... dark="#005f5f", ... )

Source code in src/instapay_eg/integrations/qrcode.py
def save_qr(
    link: str,
    path: str | Path,
    *,
    scale: int = 10,
    dark: str = "#1a1a2e",
    light: str = "#ffffff",
    file_format: str | None = None,
) -> None:
    """Save a QR code image for *link* to *path* on disk.

    The output format is inferred from the file extension of *path* (e.g.
    ``payment.png`` → PNG, ``payment.svg`` → SVG) unless *file_format* is
    given explicitly.

    Args:
        link: A validated ``https://ipn.eg`` payment URL.
        path: Destination file path.  Parent directories must already exist.
        scale: Pixel size of each QR module.  ``10`` produces a ~330x330 px
            image at the default data density.
        dark: Hex colour for the dark modules (default: deep navy ``#1a1a2e``).
        light: Hex colour for the light modules (default: white ``#ffffff``).
        file_format: Override the output format (e.g. ``'png'``, ``'svg'``).
            When ``None``, the format is inferred from *path*.

    Example:
        >>> save_qr(
        ...     "https://ipn.eg/S/alice/instapay/2DcFGv",
        ...     "alice_qr.png",
        ...     scale=12,
        ...     dark="#005f5f",
        ... )
    """
    qr = generate_qr(link)
    qr.save(
        str(path),
        scale=scale,
        dark=dark,
        light=light,
        kind=file_format,
    )

qr_as_bytes

qr_as_bytes(
    link: str,
    *,
    file_format: str = "png",
    scale: int = 10,
    dark: str = "#1a1a2e",
    light: str = "#ffffff",
) -> bytes

Return the QR code image for link as raw bytes.

Parameters:

Name Type Description Default
link str

A validated https://ipn.eg payment URL.

required
file_format str

Image format. 'png' (default) or 'svg'.

'png'
scale int

Pixel size of each QR module.

10
dark str

Hex colour for the dark modules.

'#1a1a2e'
light str

Hex colour for the light modules.

'#ffffff'

Returns:

Type Description
bytes

Raw image bytes in the requested format.

Example

png_bytes = qr_as_bytes("https://ipn.eg/S/alice/instapay/2DcFGv") with open("payment.png", "wb") as f: ... f.write(png_bytes)

Source code in src/instapay_eg/integrations/qrcode.py
def qr_as_bytes(
    link: str,
    *,
    file_format: str = "png",
    scale: int = 10,
    dark: str = "#1a1a2e",
    light: str = "#ffffff",
) -> bytes:
    """Return the QR code image for *link* as raw bytes.

    Args:
        link: A validated ``https://ipn.eg`` payment URL.
        file_format: Image format.  ``'png'`` (default) or ``'svg'``.
        scale: Pixel size of each QR module.
        dark: Hex colour for the dark modules.
        light: Hex colour for the light modules.

    Returns:
        Raw image bytes in the requested format.

    Example:
        >>> png_bytes = qr_as_bytes("https://ipn.eg/S/alice/instapay/2DcFGv")
        >>> with open("payment.png", "wb") as f:
        ...     f.write(png_bytes)
    """
    qr = generate_qr(link)
    buf = io.BytesIO()
    qr.save(buf, kind=file_format, scale=scale, dark=dark, light=light)
    return buf.getvalue()

qr_as_base64

qr_as_base64(
    link: str,
    *,
    file_format: str = "png",
    scale: int = 10,
    dark: str = "#1a1a2e",
    light: str = "#ffffff",
) -> str

Return the QR code as a base64-encoded string, ready for a JSON API.

The returned string can be embedded directly in an HTML <img> tag or returned from a REST endpoint without needing to write a file to disk.

Parameters:

Name Type Description Default
link str

A validated https://ipn.eg payment URL.

required
file_format str

Image format used for encoding. 'png' (default).

'png'
scale int

Pixel size of each QR module.

10
dark str

Hex colour for the dark modules.

'#1a1a2e'
light str

Hex colour for the light modules.

'#ffffff'

Returns:

Type Description
str

A base64-encoded string of the QR code image (no data: prefix).

Example

b64 = qr_as_base64("https://ipn.eg/S/alice/instapay/2DcFGv") html = f''

Source code in src/instapay_eg/integrations/qrcode.py
def qr_as_base64(
    link: str,
    *,
    file_format: str = "png",
    scale: int = 10,
    dark: str = "#1a1a2e",
    light: str = "#ffffff",
) -> str:
    """Return the QR code as a base64-encoded string, ready for a JSON API.

    The returned string can be embedded directly in an HTML ``<img>`` tag or
    returned from a REST endpoint without needing to write a file to disk.

    Args:
        link: A validated ``https://ipn.eg`` payment URL.
        file_format: Image format used for encoding.  ``'png'`` (default).
        scale: Pixel size of each QR module.
        dark: Hex colour for the dark modules.
        light: Hex colour for the light modules.

    Returns:
        A base64-encoded string of the QR code image (no ``data:`` prefix).

    Example:
        >>> b64 = qr_as_base64("https://ipn.eg/S/alice/instapay/2DcFGv")
        >>> html = f'<img src="data:image/png;base64,{b64}" />'
    """
    raw = qr_as_bytes(
        link, file_format=file_format, scale=scale, dark=dark, light=light
    )
    return base64.b64encode(raw).decode("ascii")

qr_as_svg_string

qr_as_svg_string(
    link: str,
    *,
    scale: int = 10,
    dark: str = "#1a1a2e",
    light: str = "#ffffff",
) -> str

Return the QR code as an inline SVG string.

The returned string can be injected directly into an HTML document without a separate HTTP request, and scales perfectly at any resolution.

Parameters:

Name Type Description Default
link str

A validated https://ipn.eg payment URL.

required
scale int

Size multiplier for the SVG viewport.

10
dark str

Hex colour for the dark modules.

'#1a1a2e'
light str

Hex colour for the light modules.

'#ffffff'

Returns:

Type Description
str

A complete <svg>...</svg> string.

Example

svg = qr_as_svg_string("https://ipn.eg/S/alice/instapay/2DcFGv") html_response = f"

{svg}
"

Source code in src/instapay_eg/integrations/qrcode.py
def qr_as_svg_string(
    link: str,
    *,
    scale: int = 10,
    dark: str = "#1a1a2e",
    light: str = "#ffffff",
) -> str:
    """Return the QR code as an inline SVG string.

    The returned string can be injected directly into an HTML document without
    a separate HTTP request, and scales perfectly at any resolution.

    Args:
        link: A validated ``https://ipn.eg`` payment URL.
        scale: Size multiplier for the SVG viewport.
        dark: Hex colour for the dark modules.
        light: Hex colour for the light modules.

    Returns:
        A complete ``<svg>...</svg>`` string.

    Example:
        >>> svg = qr_as_svg_string("https://ipn.eg/S/alice/instapay/2DcFGv")
        >>> html_response = f"<div class='qr-container'>{svg}</div>"
    """
    qr = generate_qr(link)
    buf = io.BytesIO()
    qr.save(buf, kind="svg", scale=scale, dark=dark, light=light, svgid="instapay-qr")
    return buf.getvalue().decode("utf-8")

Django

instapay_eg.integrations.django

Django integration for instapay-eg.

Provides a Django ORM model field and a Django form field for seamless integration with Django applications.

Usage in a Django model::

from django.db import models
from instapay_eg.integrations.django import InstaPayLinkField

class UserProfile(models.Model):
    instapay_link = InstaPayLinkField(blank=True, null=True)

Usage in a Django form::

from django import forms
from instapay_eg.integrations.django import InstaPayHandleFormField

class PaymentForm(forms.Form):
    recipient = InstaPayHandleFormField()

Raises:

Type Description
ImportError

If django is not installed. Install with pip install "instapay-eg[django]".

InstaPayLinkField

Bases: CharField

A Django model field that stores a validated InstaPay payment link.

On full_clean() / save(), the field automatically:

  1. Extracts the https://ipn.eg URL from the raw input (so users can paste the full share-sheet text directly).
  2. Runs all security checks (HTTPS, domain, phishing patterns).
  3. Stores only the clean URL - not the surrounding text.

Parameters:

Name Type Description Default
max_length int

Column length. Defaults to 500, which comfortably fits any InstaPay URL.

500
**kwargs Any

Passed directly to :class:django.db.models.CharField.

{}

Example::

class Professional(models.Model):
    instapay_link = InstaPayLinkField(blank=True, null=True)
Source code in src/instapay_eg/integrations/django.py
class InstaPayLinkField(models.CharField):
    """A Django model field that stores a validated InstaPay payment link.

    On ``full_clean()`` / ``save()``, the field automatically:

    1. Extracts the ``https://ipn.eg`` URL from the raw input (so users can
       paste the full share-sheet text directly).
    2. Runs all security checks (HTTPS, domain, phishing patterns).
    3. Stores only the clean URL - not the surrounding text.

    Args:
        max_length: Column length.  Defaults to ``500``, which comfortably
            fits any InstaPay URL.
        **kwargs: Passed directly to :class:`django.db.models.CharField`.

    Example::

        class Professional(models.Model):
            instapay_link = InstaPayLinkField(blank=True, null=True)
    """

    description = "A validated Egyptian InstaPay payment link."

    def __init__(self, *args: Any, max_length: int = 500, **kwargs: Any) -> None:
        """Initialise the field with a sensible default ``max_length``."""
        super().__init__(*args, max_length=max_length, **kwargs)

    def clean(self, value: Any, model_instance: Any) -> str | None:
        """Validate and clean the field value.

        Args:
            value: The raw input value.
            model_instance: The model instance being saved.

        Returns:
            The validated InstaPay URL, or ``None`` if the field is nullable
            and the value is empty.

        Raises:
            ValidationError: If the link is missing, insecure, or malformed.
        """
        value = super().clean(value, model_instance)
        if not value:
            return value
        try:
            return parse_text(str(value)).link
        except InstaPayError as exc:
            raise ValidationError(str(exc)) from exc

__init__

__init__(
    *args: Any, max_length: int = 500, **kwargs: Any
) -> None

Initialise the field with a sensible default max_length.

Source code in src/instapay_eg/integrations/django.py
def __init__(self, *args: Any, max_length: int = 500, **kwargs: Any) -> None:
    """Initialise the field with a sensible default ``max_length``."""
    super().__init__(*args, max_length=max_length, **kwargs)

clean

clean(value: Any, model_instance: Any) -> str | None

Validate and clean the field value.

Parameters:

Name Type Description Default
value Any

The raw input value.

required
model_instance Any

The model instance being saved.

required

Returns:

Type Description
str | None

The validated InstaPay URL, or None if the field is nullable

str | None

and the value is empty.

Raises:

Type Description
ValidationError

If the link is missing, insecure, or malformed.

Source code in src/instapay_eg/integrations/django.py
def clean(self, value: Any, model_instance: Any) -> str | None:
    """Validate and clean the field value.

    Args:
        value: The raw input value.
        model_instance: The model instance being saved.

    Returns:
        The validated InstaPay URL, or ``None`` if the field is nullable
        and the value is empty.

    Raises:
        ValidationError: If the link is missing, insecure, or malformed.
    """
    value = super().clean(value, model_instance)
    if not value:
        return value
    try:
        return parse_text(str(value)).link
    except InstaPayError as exc:
        raise ValidationError(str(exc)) from exc

InstaPayHandleFormField

Bases: CharField

A Django form field that validates an InstaPay handle.

Accepts both 'alice' and 'alice@instapay' - the @instapay suffix is stripped and normalised automatically.

Example::

class PaymentForm(forms.Form):
    recipient = InstaPayHandleFormField(
        label="InstaPay Handle",
        help_text="Enter the recipient's @instapay handle.",
    )
Source code in src/instapay_eg/integrations/django.py
class InstaPayHandleFormField(forms.CharField):
    """A Django form field that validates an InstaPay handle.

    Accepts both ``'alice'`` and ``'alice@instapay'`` - the ``@instapay``
    suffix is stripped and normalised automatically.

    Example::

        class PaymentForm(forms.Form):
            recipient = InstaPayHandleFormField(
                label="InstaPay Handle",
                help_text="Enter the recipient's @instapay handle.",
            )
    """

    def validate(self, value: str) -> None:
        """Validate that *value* is a correctly formatted InstaPay handle.

        Args:
            value: The submitted form value.

        Raises:
            ValidationError: If the handle contains invalid characters.
        """
        super().validate(value)
        if value:
            clean = normalize_handle(value)
            if not is_valid_handle(clean):
                raise ValidationError(
                    f"{value!r} is not a valid InstaPay handle. "
                    "Handles may only contain letters, digits, underscores, "
                    "hyphens, and dots."
                )

    def clean(self, value: str) -> str:
        """Normalise and validate the handle.

        Args:
            value: The submitted form value.

        Returns:
            The normalised handle (without ``@instapay`` suffix).
        """
        value = super().clean(value)
        return normalize_handle(value) if value else value

validate

validate(value: str) -> None

Validate that value is a correctly formatted InstaPay handle.

Parameters:

Name Type Description Default
value str

The submitted form value.

required

Raises:

Type Description
ValidationError

If the handle contains invalid characters.

Source code in src/instapay_eg/integrations/django.py
def validate(self, value: str) -> None:
    """Validate that *value* is a correctly formatted InstaPay handle.

    Args:
        value: The submitted form value.

    Raises:
        ValidationError: If the handle contains invalid characters.
    """
    super().validate(value)
    if value:
        clean = normalize_handle(value)
        if not is_valid_handle(clean):
            raise ValidationError(
                f"{value!r} is not a valid InstaPay handle. "
                "Handles may only contain letters, digits, underscores, "
                "hyphens, and dots."
            )

clean

clean(value: str) -> str

Normalise and validate the handle.

Parameters:

Name Type Description Default
value str

The submitted form value.

required

Returns:

Type Description
str

The normalised handle (without @instapay suffix).

Source code in src/instapay_eg/integrations/django.py
def clean(self, value: str) -> str:
    """Normalise and validate the handle.

    Args:
        value: The submitted form value.

    Returns:
        The normalised handle (without ``@instapay`` suffix).
    """
    value = super().clean(value)
    return normalize_handle(value) if value else value