Skip to content

components.payment_gateway.public.authorizations

This module defines the public API for the transfers subcomponent.

Only business logic is exposed here. Basic entities and enums are exposed in separate modules to avoid loading the entire subcomponent with its models and dependencies when they are not needed.

Attributes

ExpenseCategoryId module-attribute

ExpenseCategoryId = NewType('ExpenseCategoryId', UUID)

LineOfCreditId module-attribute

LineOfCreditId = NewType('LineOfCreditId', UUID)

Classes

DuplicateExpenseCategoryException

Bases: PaymentAuthorizationsException

Exception raised when trying to create a duplicate Expense Category.

DuplicateLineOfCreditException

Bases: PaymentAuthorizationsException

Exception raised when trying to create a duplicate Line of Credit.

ExpenseCategory dataclass

ExpenseCategory(id, code, name, description, reference)

An Expense Category is a high level concept to categorize expenses (or payments), as its name implies. A given payment can match several categories, for example food expenses, grocery stores, payments made abroad, payments made on Sunday, etc.

Expense Limits are typically in limited number for a given application. They are not linked to a specific card, account, or payment method.

Each payment must belong to at least one Expense Category to be authorized.

Attributes

code instance-attribute
code

Machine-readable code of the category, unique within the system.

description instance-attribute
description

Description of the category.

id instance-attribute
id
name instance-attribute
name

Human-readable name of the category.

reference instance-attribute
reference

Application-specific reference to the category.

ExpenseCategoryLogic

This class is the public interface to the Expense Category logic.

Functions

create_expense_category
create_expense_category(
    session, /, code, name, description=None, reference=None
)

Create an expense category.

Parameters:

Name Type Description Default
session Session

The session to use for the database operations.

required
code str

The code of the expense category (must be unique).

required
name str

The name of the expense category.

required
description str | None

The description of the expense category.

None
reference str | None

The reference of the expense category.

None

Returns:

Type Description
ExpenseCategoryId

ID of the created expense category.

Source code in components/payment_gateway/subcomponents/authorizations/protected/business_logic/expense_categories.py
@obs.api_call()
def create_expense_category(
    self,
    session: Session,
    /,
    code: str,
    name: str,
    description: str | None = None,
    reference: str | None = None,
) -> ExpenseCategoryId:
    """Create an expense category.

    Args:
        session: The session to use for the database operations.
        code: The code of the expense category (must be unique).
        name: The name of the expense category.
        description: The description of the expense category.
        reference: The reference of the expense category.

    Returns:
        ID of the created expense category.
    """
    with raise_if_duplicate_expense_category(code):
        expense_category = ExpenseCategoryModelBroker.create_expense_category(
            session,
            code=code,
            name=name,
            description=description,
            reference=reference,
        )
        return ExpenseCategoryId(expense_category.id)
get_expense_category_id_by_code
get_expense_category_id_by_code(session, /, code)

Get expense category ID by its code.

Parameters:

Name Type Description Default
session Session

The session to use for the database operations.

required
code str

Expense category code.

required

Returns:

Type Description
ExpenseCategoryId

Expense category ID.

Source code in components/payment_gateway/subcomponents/authorizations/protected/business_logic/expense_categories.py
@obs.api_call()
def get_expense_category_id_by_code(
    self,
    session: Session,
    /,
    code: str,
) -> ExpenseCategoryId:
    """Get expense category ID by its code.

    Args:
        session: The session to use for the database operations.
        code: Expense category code.

    Returns:
        Expense category ID.
    """
    with raise_if_expense_category_not_found(code):
        return ExpenseCategoryId(
            ExpenseCategoryModelBroker.get_expense_category_id_by_code(
                session,
                code,
            )
        )
get_expense_category_ids_by_codes
get_expense_category_ids_by_codes(session, /, codes)

Get expense category IDs by codes.

Parameters:

Name Type Description Default
session Session

The session to use for the database operations.

required
codes set[str]

Set of expense category codes.

required

Returns:

Type Description
set[ExpenseCategoryId]

Set of expense category IDs.

Warning

The length of the output set may be less than the length of the input set if some entries do not exist in the database.

Source code in components/payment_gateway/subcomponents/authorizations/protected/business_logic/expense_categories.py
@obs.api_call()
def get_expense_category_ids_by_codes(
    self,
    session: Session,
    /,
    codes: set[str],
) -> set[ExpenseCategoryId]:
    """Get expense category IDs by codes.

    Args:
        session: The session to use for the database operations.
        codes: Set of expense category codes.

    Returns:
        Set of expense category IDs.

    Warning:
        The length of the output set may be less than the length of the
        input set if some entries do not exist in the database.
    """
    return {
        ExpenseCategoryId(expense_category_id)
        for expense_category_id in ExpenseCategoryModelBroker.list_expense_category_ids_by_codes(
            session,
            list(codes),
        )
    }

ExpenseCategoryNotFoundException

Bases: PaymentAuthorizationsException

Exception raised when trying to use a non-existing Expense Category.

ExpenseCategoryResolver

Bases: ABC

This abstract class defines the interface responsible for resolving the expense categories for a pending transaction.

Warning

Implementations of this class should be stateless and fail-safe, as they are used for critical idempotent operations in a real-time context.

Functions

resolve_expense_categories abstractmethod
resolve_expense_categories(pending_transaction)

Resolve the expense category codes for a pending transaction.

Parameters:

Name Type Description Default
pending_transaction PendingTransaction

The pending transaction to resolve the expense categories for.

required

Returns:

Type Description
set[str] | None

The matching expense category codes, or None if no expense category could be resolved.

Source code in components/payment_gateway/subcomponents/authorizations/protected/resolvers.py
@abstractmethod
def resolve_expense_categories(
    self, pending_transaction: PendingTransaction
) -> set[str] | None:
    """
    Resolve the expense category codes for a pending transaction.

    Args:
        pending_transaction: The pending transaction to resolve the expense
            categories for.

    Returns:
        The matching expense category codes, or None if no expense category
            could be resolved.
    """
    raise NotImplementedError

LineOfCredit dataclass

LineOfCredit(
    id,
    expense_category_id,
    owner_type,
    owner_ref,
    credits_limit,
    expensed_credits,
    available_credits,
)

A Line of Credit is a high level concept to manage expenses made by a given owner. Each Line of Credit belongs to an Expense Category.

For each Expense Category a payment belongs to, there must be a matching Line of Credit with sufficient credits to cover the payment.

Attributes

available_credits instance-attribute
available_credits

Available credits for the Line of Credit (in minor units).

credits_limit instance-attribute
credits_limit

Credits limit for the Line of Credit (in minor units).

expense_category_id instance-attribute
expense_category_id

Category of expenses covered by the line of credit

expensed_credits instance-attribute
expensed_credits

Expensed credits for the Line of Credit (in minor units).

id instance-attribute
id
owner_ref instance-attribute
owner_ref

Application-specific reference to the owner of the line of credit.

owner_type instance-attribute
owner_type

Application-specific type of owner for the line of credit.

LineOfCreditLogic

This class is the public interface to the Line of Credit logic.

Functions

create_line_of_credit
create_line_of_credit(
    session,
    /,
    owner_type,
    owner_ref,
    expense_category_id,
    credits_limit=0,
    expensed_credits=0,
)

Create a Line of Credit.

Parameters:

Name Type Description Default
session Session

The session to use for the database operations.

required
owner_type str

Application-specific type of owner for the Line of Credit.

required
owner_ref UUID

Application-specific reference to the owner of the Line of Credit.

required
expense_category_id ExpenseCategoryId

ID of the Expense Category the Line of Credit belongs to.

required
credits_limit int

The limit of credits for the Line of Credit (in minor units).

0
expensed_credits int

The amount of credits already expensed (in minor units).

0

Returns:

Type Description
LineOfCreditId

ID of the created Line of Credit.

Source code in components/payment_gateway/subcomponents/authorizations/protected/business_logic/lines_of_credit.py
@obs.api_call()
def create_line_of_credit(
    self,
    session: Session,
    /,
    owner_type: str,
    owner_ref: UUID,
    expense_category_id: ExpenseCategoryId,
    credits_limit: int = 0,
    expensed_credits: int = 0,
) -> LineOfCreditId:
    """Create a Line of Credit.

    Args:
        session: The session to use for the database operations.
        owner_type: Application-specific type of owner for the Line of
            Credit.
        owner_ref: Application-specific reference to the owner of the Line
            of Credit.
        expense_category_id: ID of the Expense Category the Line of Credit
            belongs to.
        credits_limit: The limit of credits for the Line of Credit (in
            minor units).
        expensed_credits: The amount of credits already expensed (in minor
            units).

    Returns:
        ID of the created Line of Credit.
    """
    with raise_if_duplicate_line_of_credit(
        owner_type, owner_ref, expense_category_id
    ):
        line_of_credit = LineOfCreditModelBroker.create_line_of_credit(
            session,
            owner_type=owner_type,
            owner_ref=owner_ref,
            expense_category_id=expense_category_id,
        )
        ExpenseTrackerModelBroker.create_expense_tracker(
            session,
            line_of_credit_id=line_of_credit.id,
            credits_limit=credits_limit,
            expensed_credits=expensed_credits,
        )
        return LineOfCreditId(line_of_credit.id)
get_line_of_credit
get_line_of_credit(session, /, id)

Get a Line of Credit by its ID.

Parameters:

Name Type Description Default
session Session

The session to use for the database operations.

required
id LineOfCreditId

ID of the Line of Credit.

required

Returns:

Type Description
LineOfCredit

Line of Credit entity.

Source code in components/payment_gateway/subcomponents/authorizations/protected/business_logic/lines_of_credit.py
@obs.api_call()
def get_line_of_credit(
    self,
    session: Session,
    /,
    id: LineOfCreditId,
) -> LineOfCredit:
    """Get a Line of Credit by its ID.

    Args:
        session: The session to use for the database operations.
        id: ID of the Line of Credit.

    Returns:
        Line of Credit entity.
    """
    with raise_if_line_of_credit_not_found(id):
        line_of_credit = LineOfCreditModelBroker.get_line_of_credit(
            session,
            id=id,
        )
        return _to_line_of_credit(line_of_credit)
get_line_of_credit_id_for_owner_and_expense_category_id
get_line_of_credit_id_for_owner_and_expense_category_id(
    session, /, owner_type, owner_ref, expense_category_id
)

Get Line of Credit ID for a specific owner and Expense Category ID.

Parameters:

Name Type Description Default
session Session

The session to use for the database operations.

required
owner_type str

Application-specific type of owner for the Line of Credit.

required
owner_ref UUID

Application-specific reference to the owner of the Line of Credit.

required
expense_category_id ExpenseCategoryId

Expense Category IDs to resolve.

required

Returns:

Type Description
LineOfCreditId | None

Line of Credit ID or None if none found.

Source code in components/payment_gateway/subcomponents/authorizations/protected/business_logic/lines_of_credit.py
@obs.api_call()
def get_line_of_credit_id_for_owner_and_expense_category_id(
    self,
    session: Session,
    /,
    owner_type: str,
    owner_ref: UUID,
    expense_category_id: ExpenseCategoryId,
) -> LineOfCreditId | None:
    """Get Line of Credit ID for a specific owner and Expense Category ID.

    Args:
        session: The session to use for the database operations.
        owner_type: Application-specific type of owner for the Line of Credit.
        owner_ref: Application-specific reference to the owner of the Line of Credit.
        expense_category_id: Expense Category IDs to resolve.

    Returns:
        Line of Credit ID or None if none found.
    """
    line_of_credit_id = LineOfCreditModelBroker.find_line_of_credit_id_for_owner_and_expense_category_id(
        session,
        owner_type=owner_type,
        owner_ref=owner_ref,
        expense_category_id=expense_category_id,
    )
    return LineOfCreditId(line_of_credit_id) if line_of_credit_id else None
get_line_of_credit_ids_for_owner_and_expense_category_ids
get_line_of_credit_ids_for_owner_and_expense_category_ids(
    session, /, owner_type, owner_ref, expense_category_ids
)

Get Line of Credit IDs for a given owner and Expense Category IDs.

Parameters:

Name Type Description Default
session Session

The session to use for the database operations.

required
owner_type str

Application-specific type of owner for the Line of Credit.

required
owner_ref UUID

Application-specific reference to the owner of the Line of Credit.

required
expense_category_ids set[ExpenseCategoryId]

Set of Expense Category IDs.

required

Returns:

Type Description
set[LineOfCreditId]

Set of Line of Credit IDs.

Warning

The length of the output set may be less than the length of the input set if some entries do not exist in the database.

Source code in components/payment_gateway/subcomponents/authorizations/protected/business_logic/lines_of_credit.py
@obs.api_call()
def get_line_of_credit_ids_for_owner_and_expense_category_ids(
    self,
    session: Session,
    /,
    owner_type: str,
    owner_ref: UUID,
    expense_category_ids: set[ExpenseCategoryId],
) -> set[LineOfCreditId]:
    """Get Line of Credit IDs for a given owner and Expense Category IDs.

    Args:
        session: The session to use for the database operations.
        owner_type: Application-specific type of owner for the Line of Credit.
        owner_ref: Application-specific reference to the owner of the Line of Credit.
        expense_category_ids: Set of Expense Category IDs.

    Returns:
        Set of Line of Credit IDs.

    Warning:
        The length of the output set may be less than the length of the
        input set if some entries do not exist in the database.
    """
    return {
        LineOfCreditId(line_of_credit_id)
        for line_of_credit_id in LineOfCreditModelBroker.list_line_of_credit_ids_for_owner_and_expense_category_ids(
            session,
            owner_type=owner_type,
            owner_ref=owner_ref,
            expense_category_ids=list(expense_category_ids),
        )
    }
get_lines_of_credit_for_owner
get_lines_of_credit_for_owner(
    session, /, owner_type, owner_ref
)

Get all Line of Credit IDs for a specific owner.

Parameters:

Name Type Description Default
session Session

The session to use for the database operations.

required
owner_type str

Application-specific type of owner for the Line of Credit.

required
owner_ref UUID

Application-specific reference to the owner of the Line of Credit.

required

Returns:

Type Description
list[LineOfCredit]

Set of Line of Credit IDs.

Source code in components/payment_gateway/subcomponents/authorizations/protected/business_logic/lines_of_credit.py
@obs.api_call()
def get_lines_of_credit_for_owner(
    self,
    session: Session,
    /,
    owner_type: str,
    owner_ref: UUID,
) -> list[LineOfCredit]:
    """Get all Line of Credit IDs for a specific owner.

    Args:
        session: The session to use for the database operations.
        owner_type: Application-specific type of owner for the Line of Credit.
        owner_ref: Application-specific reference to the owner of the Line of Credit.

    Returns:
        Set of Line of Credit IDs.
    """
    lines_of_credit = LineOfCreditModelBroker.list_lines_of_credit_for_owner(
        session,
        owner_type=owner_type,
        owner_ref=owner_ref,
    )
    return [
        _to_line_of_credit(line_of_credit) for line_of_credit in lines_of_credit
    ]
reset_credits
reset_credits(
    session,
    /,
    line_of_credit_id,
    credits_limit,
    expensed_credits,
)

Reset the credits limit and expensed credits of a Line of Credit.

Parameters:

Name Type Description Default
session Session

The session to use for the database operations.

required
line_of_credit_id LineOfCreditId

ID of the Line of Credit.

required
credits_limit int

New credits limit (in minor units).

required
expensed_credits int

New expensed credits (in minor units).

required
Source code in components/payment_gateway/subcomponents/authorizations/protected/business_logic/lines_of_credit.py
@obs.api_call()
def reset_credits(
    self,
    session: Session,
    /,
    line_of_credit_id: LineOfCreditId,
    credits_limit: int,
    expensed_credits: int,
) -> None:
    """Reset the credits limit and expensed credits of a Line of Credit.

    Args:
        session: The session to use for the database operations.
        line_of_credit_id: ID of the Line of Credit.
        credits_limit: New credits limit (in minor units).
        expensed_credits: New expensed credits (in minor units).
    """
    with raise_if_line_of_credit_not_found(line_of_credit_id):
        ExpenseTrackerModelBroker.reset_credits(
            session,
            line_of_credit_id=line_of_credit_id,
            credits_limit=credits_limit,
            expensed_credits=expensed_credits,
        )
set_credits_limit
set_credits_limit(
    session, /, line_of_credit_id, credits_limit
)

Set the credits limit of a Line of Credit.

Parameters:

Name Type Description Default
session Session

The session to use for the database operations.

required
line_of_credit_id LineOfCreditId

ID of the Line of Credit.

required
credits_limit int

New credits limit (in minor units).

required
Source code in components/payment_gateway/subcomponents/authorizations/protected/business_logic/lines_of_credit.py
@obs.api_call()
def set_credits_limit(
    self,
    session: Session,
    /,
    line_of_credit_id: LineOfCreditId,
    credits_limit: int,
) -> None:
    """Set the credits limit of a Line of Credit.

    Args:
        session: The session to use for the database operations.
        line_of_credit_id: ID of the Line of Credit.
        credits_limit: New credits limit (in minor units).
    """
    with raise_if_line_of_credit_not_found(line_of_credit_id):
        ExpenseTrackerModelBroker.set_credits_limit(
            session,
            line_of_credit_id=line_of_credit_id,
            credits_limit=credits_limit,
        )
terminate_line_of_credit
terminate_line_of_credit(session, /, line_of_credit_id)

Terminate a Line of Credit.

Parameters:

Name Type Description Default
session Session

The session to use for the database operations.

required
line_of_credit_id LineOfCreditId

ID of the Line of Credit.

required
Source code in components/payment_gateway/subcomponents/authorizations/protected/business_logic/lines_of_credit.py
@obs.api_call()
def terminate_line_of_credit(
    self,
    session: Session,
    /,
    line_of_credit_id: LineOfCreditId,
) -> None:
    """Terminate a Line of Credit.

    Args:
        session: The session to use for the database operations.
        line_of_credit_id: ID of the Line of Credit.
    """
    with raise_if_line_of_credit_not_found(line_of_credit_id):
        LineOfCreditModelBroker.terminate_line_of_credit(
            session,
            line_of_credit_id=line_of_credit_id,
        )

LineOfCreditResolver

Bases: ABC

This abstract class defines the interface responsible for resolving the lines of credit for a pending transaction.

Warning

Implementations of this class should be stateless and fail-safe, as they are used for critical idempotent operations in a real-time context.

Functions

resolve_lines_of_credit abstractmethod
resolve_lines_of_credit(
    expense_category_ids, pending_transaction
)

Resolve the line of credit IDs for a pending transaction and expense categories.

Parameters:

Name Type Description Default
expense_category_ids set[ExpenseCategoryId]

The expense categories for the pending transaction.

required
pending_transaction PendingTransaction

The pending transaction to resolve the lines of credit for.

required

Returns:

Type Description
set[LineOfCreditId] | None

The matching lines of credit IDs, or None if at least one could not be resolved.

Invariant

There is a one-to-one relationship between input expense categories and output lines of credit.

Note

We don't need to return the entities themselves, because the authorization process only depends on their ID.

Source code in components/payment_gateway/subcomponents/authorizations/protected/resolvers.py
@abstractmethod
def resolve_lines_of_credit(
    self,
    expense_category_ids: set[ExpenseCategoryId],
    pending_transaction: PendingTransaction,
) -> set[LineOfCreditId] | None:
    """
    Resolve the line of credit IDs for a pending transaction and expense categories.

    Args:
        expense_category_ids: The expense categories for the pending transaction.
        pending_transaction: The pending transaction to resolve the lines of
            credit for.

    Returns:
        The matching lines of credit IDs, or None if at least one could not
            be resolved.

    Invariant:
        There is a one-to-one relationship between input expense categories
        and output lines of credit.

    Note:
        We don't need to return the entities themselves, because the
        authorization process only depends on their ID.
    """
    raise NotImplementedError

PaymentAuthorizationsException

Bases: PaymentGatewayException

Base class for all authorizations exceptions.

PendingTransaction dataclass

PendingTransaction(
    amount,
    currency,
    card_id,
    merchant_info,
    provider,
    external_id,
)

Information about a transaction to be authorized.

Attributes

amount instance-attribute
amount

Transaction amount (positive for debit, in minor units).

card_id instance-attribute
card_id

Card used for the transaction.

currency instance-attribute
currency

Currency used for the transaction.

external_id instance-attribute
external_id

ID used by the external payment service provider to identify the transaction.

merchant_info instance-attribute
merchant_info

Merchant information.

provider instance-attribute
provider

Payment service provider that is processing the transaction.

PendingTransactionMerchantInfo dataclass

PendingTransactionMerchantInfo(
    merchant_id,
    acquirer_id,
    mcc,
    name,
    country,
    postal_code=None,
    city=None,
)

Merchant information for a pending transaction.

Note

All fields are always present during authorization, those which are None might simply be irrelevant for the merchant (e.g. e-commerce platforms may not have a postal code)

Attributes

acquirer_id instance-attribute
acquirer_id
city class-attribute instance-attribute
city = None
country instance-attribute
country

ISO 3166-1 alpha-3 country code of the merchant.

mcc instance-attribute
mcc
merchant_id instance-attribute
merchant_id
name instance-attribute
name
postal_code class-attribute instance-attribute
postal_code = None