Skip to content

Actions

components.payment_gateway.subcomponents.payments.protected.business_logic.actions.bank_transfer_actions

BankTransferActions

BankTransferActions(adyen_client, merchant_account)

Actions for bank transfer operations including SEPA direct debit payment requests.

Tags
Source code in components/payment_gateway/subcomponents/payments/protected/business_logic/actions/bank_transfer_actions.py
def __init__(
    self,
    adyen_client: "AdyenPaymentsApiClient | None",
    merchant_account: str,
) -> None:
    self._adyen_client = adyen_client
    self._merchant_account = merchant_account

adyen_client property

adyen_client

Ensures the Adyen client is available when accessing it.

create classmethod

create()

Normal factory

Source code in components/payment_gateway/subcomponents/payments/protected/business_logic/actions/bank_transfer_actions.py
@classmethod
def create(cls) -> "BankTransferActions":
    """Normal factory"""
    from shared.helpers.config import current_config
    from shared.services.payment_providers.adyen.clients.adyen_payments_api_client import (
        AdyenPaymentsApiClient,
    )
    from shared.services.payment_providers.adyen.clients.exceptions import (
        AdyenClientMissingCredentialsException,
    )

    try:
        adyen_client = AdyenPaymentsApiClient.create()
    except AdyenClientMissingCredentialsException:
        adyen_client = None

    merchant_account = current_config.get(
        "ADYEN_MERCHANT_ACCOUNT", "AlanEU_Issuing_TEST"
    )
    return cls(adyen_client, merchant_account=merchant_account)

create_null classmethod

create_null(track_adyen_requests=None)

Null factory

Source code in components/payment_gateway/subcomponents/payments/protected/business_logic/actions/bank_transfer_actions.py
@classmethod
def create_null(
    cls,
    track_adyen_requests: list[tuple[str, dict, dict]] | None = None,  # type: ignore[type-arg]
) -> "BankTransferActions":
    """Null factory"""
    from shared.services.payment_providers.adyen.clients.adyen_payments_api_client import (
        AdyenPaymentsApiClient,
    )

    return cls(
        AdyenPaymentsApiClient.create_null(track_requests=track_adyen_requests),
        merchant_account="AlanEU_Issuing_TEST",
    )

create_payment_request

create_payment_request(
    session,
    /,
    *,
    creditor_account_id,
    amount,
    reference,
    debtor_financial_instrument_id,
    description=None,
    currency="EUR",
)

Create a payment request.

Parameters:

Name Type Description Default
session Session

SQLAlchemy session to use for DB operations.

required
creditor_account_id AccountId

ID of the account that will receive the funds.

required
amount int

Payment amount in minor currency units (e.g., cents for EUR). Must be positive.

required
reference str

Unique reference for the payment request. Used for tracking and reconciliation.

required
debtor_financial_instrument_id FinancialInstrumentId

ID of the financial instrument owning the account that will be debited.

required
description str | None

Optional human-readable description for the payment. Defaults to None.

None
currency str

ISO 4217 currency code. Defaults to "EUR".

'EUR'

Returns:

Name Type Description
PaymentRequestId PaymentRequestId

ID of the newly created payment request.

Raises:

Type Description
AccountNotFoundException

If the creditor account does not exist.

ProviderNotSupportedException

If the account's workspace does not support Adyen.

Note

This operation is currently only supported for Adyen.

Source code in components/payment_gateway/subcomponents/payments/protected/business_logic/actions/bank_transfer_actions.py
@obs.api_call()
def create_payment_request(
    self,
    session: Session,
    /,
    *,
    creditor_account_id: AccountId,
    amount: int,
    reference: str,
    debtor_financial_instrument_id: FinancialInstrumentId,
    description: str | None = None,
    currency: str = "EUR",
) -> PaymentRequestId:
    """Create a payment request.

    Args:
        session: SQLAlchemy session to use for DB operations.
        creditor_account_id: ID of the account that will receive the funds.
        amount: Payment amount in minor currency units (e.g., cents for EUR). Must be positive.
        reference: Unique reference for the payment request. Used for tracking and reconciliation.
        debtor_financial_instrument_id: ID of the financial instrument owning the account that will be debited.
        description: Optional human-readable description for the payment. Defaults to None.
        currency: ISO 4217 currency code. Defaults to "EUR".

    Returns:
        PaymentRequestId: ID of the newly created payment request.

    Raises:
        AccountNotFoundException: If the creditor account does not exist.
        ProviderNotSupportedException: If the account's workspace does not support Adyen.

    Note:
        This operation is currently only supported for Adyen.
    """
    with raise_if_financial_instrument_not_found(debtor_financial_instrument_id):
        debtor_financial_instrument = (
            FinancialInstrumentModelBroker.get_financial_instrument(
                session, debtor_financial_instrument_id, with_legal_entity=True
            )
        )
    raise_on_terminated_financial_instrument(debtor_financial_instrument)
    raise_on_terminated_legal_entity(debtor_financial_instrument.legal_entity)

    with raise_if_account_not_found(creditor_account_id):
        creditor_account = AccountModelBroker.get_account(
            session, creditor_account_id
        )
    raise_on_terminated_account(creditor_account)

    # Only Adyen is supported for now
    raise_on_provider_not_supported(
        creditor_account.workspace_key, PaymentServiceProvider.adyen
    )

    # Only IBAN accounts for SEPA Direct Debit are supported for now
    raise_on_financial_instrument_type_not_supported(
        debtor_financial_instrument.instrument_type,
        [FinancialInstrumentType.IBAN_ACCOUNT],
    )
    assert isinstance(debtor_financial_instrument, IBANAccountFinancialInstrument)
    debtor_iban_data = decrypt_value(
        debtor_financial_instrument,
        IBANAccountFinancialInstrument.__table__.columns.iban_account_data,
    )

    response = self.adyen_client.payments(
        PaymentRequest(
            merchantAccount=self._merchant_account,
            reference=reference,
            amount=Amount(
                currency=currency,
                value=amount,
            ),
            paymentMethod=SepaDirectDebitDetails(
                type="sepadirectdebit",
                iban=debtor_iban_data.iban,
                ownerName=debtor_financial_instrument.legal_entity.legal_name,
            ),
            splits=[
                Split(
                    type="TopUp",
                    amount=SplitAmount(currency=currency, value=amount),
                    account=creditor_account.external_id,
                    reference=reference,
                    description=description,
                )
            ],
            returnUrl="",  # On Direct Debit no url is required
        )
    )

    payment_request = PaymentRequestModelBroker.create_payment_request(
        session,
        external_id=mandatory(response.pspReference),
        workspace_key=creditor_account.workspace_key,
        account_id=creditor_account.id,
        raw=response.model_dump(),
    )

    return PaymentRequestId(payment_request.id)