Skip to content

Commit

Permalink
17829 - EFT Service updates (#1293)
Browse files Browse the repository at this point in the history
* eft_short_names table

* 17829 - eft feature flagging, eft short names tables

* version bump

* update flags service for LD 8.x
  • Loading branch information
ochiu authored Oct 19, 2023
1 parent b0e8e57 commit f528537
Show file tree
Hide file tree
Showing 10 changed files with 169 additions and 11 deletions.
3 changes: 2 additions & 1 deletion pay-api/flags.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"flagValues": {
"string-flag": "a string value",
"bool-flag": true,
"integer-flag": 10
"integer-flag": 10,
"enable-eft-payment-method": true
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
"""17829-eft-shortnames
Revision ID: 194cdd7cf986
Revises: 456234145e5e
Create Date: 2023-10-18 08:23:04.207463
"""
from alembic import op
import sqlalchemy as sa

from pay_api import db

# revision identifiers, used by Alembic.
revision = '194cdd7cf986'
down_revision = '456234145e5e'
branch_labels = None
depends_on = None


def upgrade():
op.create_table('eft_short_names',
sa.Column('id', sa.Integer(), nullable=False, primary_key=True),
sa.Column('short_name', sa.String(), nullable=False, unique=True),
sa.Column('auth_account_id', sa.String(length=50), nullable=True),
sa.Column('created_on', sa.DateTime(), nullable=False),
)

op.create_index(op.f('ix_eft_short_names_auth_account_id'), 'eft_short_names', ['auth_account_id'], unique=False)


def downgrade():
op.drop_table('eft_short_names')
48 changes: 48 additions & 0 deletions pay-api/src/pay_api/models/eft_short_names.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Copyright © 2023 Province of British Columbia
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Model to handle EFT TDI17 short name to BCROS account mapping."""

from datetime import datetime

from .base_model import BaseModel
from .db import db


class EFTShortnames(BaseModel): # pylint: disable=too-many-instance-attributes
"""This class manages the EFT short name to auth account mapping."""

__tablename__ = 'eft_short_names'
# this mapper is used so that new and old versions of the service can be run simultaneously,
# making rolling upgrades easier
# This is used by SQLAlchemy to explicitly define which fields we're interested
# so it doesn't freak out and say it can't map the structure if other fields are present.
# This could occur from a failed deploy or during an upgrade.
# The other option is to tell SQLAlchemy to ignore differences, but that is ambiguous
# and can interfere with Alembic upgrades.
#
# NOTE: please keep mapper names in alpha-order, easier to track that way
# Exception, id is always first, _fields first
__mapper_args__ = {
'include_properties': [
'id',
'auth_account_id',
'created_on',
'short_name'
]
}

id = db.Column(db.Integer, primary_key=True, autoincrement=True)
auth_account_id = db.Column('auth_account_id', db.DateTime, nullable=True, index=True)
created_on = db.Column('created_on', db.DateTime, nullable=False, default=datetime.now)
short_name = db.Column('short_name', db.String, nullable=False, index=True)
9 changes: 9 additions & 0 deletions pay-api/src/pay_api/services/eft_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
from pay_api.utils.enums import PaymentMethod

from .deposit_service import DepositService
from .invoice import Invoice
from .invoice_reference import InvoiceReference
from .payment_account import PaymentAccount
from .payment_line_item import PaymentLineItem


class EftService(DepositService):
Expand All @@ -24,3 +28,8 @@ class EftService(DepositService):
def get_payment_method_code(self):
"""Return EFT as the system code."""
return PaymentMethod.EFT.value

def create_invoice(self, payment_account: PaymentAccount, line_items: [PaymentLineItem], invoice: Invoice,
**kwargs) -> InvoiceReference:
"""Return a static invoice number for direct pay."""
# Do nothing here as the invoice references will be created later for eft payment reconciliations (TDI17).
14 changes: 5 additions & 9 deletions pay-api/src/pay_api/services/flags.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from flask import current_app
from ldclient import get as ldclient_get, set_config as ldclient_set_config # noqa: I001
from ldclient.config import Config # noqa: I005
from ldclient import Context
from ldclient.integrations import Files

from pay_api.utils import user_context
Expand Down Expand Up @@ -76,18 +77,13 @@ def _get_client(self):

@staticmethod
def _get_anonymous_user():
return {
'key': 'anonymous'
}
return Context.create('anonymous')

@staticmethod
def _user_as_key(user: user_context):
user_json = {
'key': user.sub,
'userName': user.user_name,
'firstName': user.first_name
}
return user_json
return Context.builder(user.sub)\
.set('userName', user.user_name)\
.set('firstName', user.first_name).build()

def is_on(self, flag: str, default: bool = False, user: user_context = None) -> bool:
"""Assert that the flag is set for this user."""
Expand Down
5 changes: 5 additions & 0 deletions pay-api/src/pay_api/services/payment_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

from .base_payment_system import PaymentSystemService
from .fee_schedule import FeeSchedule
from .flags import flags
from .invoice import Invoice
from .invoice_reference import InvoiceReference
from .payment import Payment
Expand Down Expand Up @@ -64,6 +65,10 @@ def create_invoice(cls, payment_request: Tuple[Dict[str, Any]], authorization: T

payment_account = cls._find_payment_account(authorization)
payment_method = _get_payment_method(payment_request, payment_account)

if payment_method == PaymentMethod.EFT.value and not flags.is_on('enable-eft-payment-method', default=False):
raise BusinessException(Error.INVALID_PAYMENT_METHOD)

current_app.logger.info(f'Creating Payment Request : '
f'{payment_method}, {corp_type}, {business_identifier}, '
f'{payment_account.auth_account_id}')
Expand Down
2 changes: 2 additions & 0 deletions pay-api/src/pay_api/utils/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ class Error(Enum):

INVALID_PAYMENT_ID = 'INVALID_PAYMENT_ID', HTTPStatus.BAD_REQUEST

INVALID_PAYMENT_METHOD = 'INVALID_PAYMENT_METHOD', HTTPStatus.BAD_REQUEST

INVALID_TRANSACTION = 'INVALID_TRANSACTION', HTTPStatus.BAD_REQUEST

INVALID_REDIRECT_URI = 'INVALID_REDIRECT_URI', HTTPStatus.BAD_REQUEST
Expand Down
2 changes: 1 addition & 1 deletion pay-api/src/pay_api/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@
Development release segment: .devN
"""

__version__ = '1.20.1' # pylint: disable=invalid-name
__version__ = '1.20.2' # pylint: disable=invalid-name
48 changes: 48 additions & 0 deletions pay-api/tests/unit/models/test_eft_short_names.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Copyright © 2023 Province of British Columbia
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Tests to assure the EFT File model.
Test-Suite to ensure that the EFT File model is working as expected.
"""
from datetime import datetime

from pay_api.models.eft_short_names import EFTShortnames as EFTShortnamesModel


def test_eft_short_name_defaults(session):
"""Assert eft short names defaults are stored."""
eft_short_name = EFTShortnamesModel()
eft_short_name.short_name = 'ABC'
eft_short_name.save()

assert eft_short_name.id is not None
assert eft_short_name.short_name == 'ABC'
assert eft_short_name.created_on.date() == datetime.now().date()
assert eft_short_name.auth_account_id is None


def test_eft_short_names_all_attributes(session):
"""Assert all eft short names attributes are stored."""
eft_short_name = EFTShortnamesModel()
eft_short_name.short_name = 'ABC'
eft_short_name.auth_account_id = '1234'
eft_short_name.save()

assert eft_short_name.id is not None

eft_short_name = EFTShortnamesModel.find_by_id(eft_short_name.id)
assert eft_short_name.short_name == 'ABC'
assert eft_short_name.auth_account_id == '1234'
assert eft_short_name.created_on.date() == datetime.now().date()
17 changes: 17 additions & 0 deletions pay-api/tests/unit/services/test_payment_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
from pay_api.services.payment_service import PaymentService
from pay_api.utils.enums import InvoiceStatus, PaymentMethod, PaymentStatus, RoutingSlipStatus
from requests.exceptions import ConnectionError, ConnectTimeout, HTTPError

from pay_api.utils.errors import Error
from tests.utilities.base_test import (
factory_invoice, factory_invoice_reference, factory_payment, factory_payment_account, factory_payment_line_item,
factory_payment_transaction, factory_routing_slip, get_auth_basic_user, get_auth_premium_user, get_auth_staff,
Expand Down Expand Up @@ -330,6 +332,21 @@ def test_create_eft_payment(session, public_user_mock):
assert payment_response.get('status_code') == 'CREATED'


def test_create_eft_payment_ff_disabled(session, public_user_mock):
"""Assert that the payment method EFT feature flag properly disables record creation."""
factory_payment_account(payment_method_code=PaymentMethod.EFT.value).save()

with patch('pay_api.services.payment_service.flags.is_on', return_value=False):
with pytest.raises(BusinessException) as exception:
PaymentService.create_invoice(
get_payment_request_with_service_fees(
business_identifier='CP0002000'),
get_auth_premium_user())

assert exception is not None
assert exception.value.code == Error.INVALID_PAYMENT_METHOD.name


def test_create_wire_payment(session, public_user_mock):
"""Assert that the payment records are created."""
factory_payment_account(payment_method_code=PaymentMethod.WIRE.value).save()
Expand Down

0 comments on commit f528537

Please sign in to comment.