Skip to content

Commit

Permalink
Feature flag DIRECT_PAY refunds & Update version, plus clean out pers…
Browse files Browse the repository at this point in the history
…onal branch references (#987)

* Update version, plus clean out personal branch references.

* Fix json schema

* Fix docker file

* New Feature flag ENABLE_PAYBC_AUTOMATED_REFUNDS

* Minor unit test fix.
  • Loading branch information
seeker25 authored Sep 14, 2022
1 parent a1a893d commit a90fe46
Show file tree
Hide file tree
Showing 9 changed files with 48 additions and 33 deletions.
14 changes: 7 additions & 7 deletions jobs/payment-jobs/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
-e git+https://github.com/bcgov/sbc-common-components.git@4efa1e6664dff321030eca7448a7d5af91c2f355#egg=sbc_common_components&subdirectory=python
-e git+https://github.com/seeker25/sbc-pay.git@f8fb8a8cb3990470446548d5ad7ed9d7c98e4b52#egg=pay_api&subdirectory=pay-api
-e git+https://github.com/bcgov/sbc-pay.git@95cd9254ed89d01cd571918aa845bbe7b85f348b#egg=pay_api&subdirectory=pay-api
Flask-Caching==2.0.1
Flask-Migrate==2.7.0
Flask-Moment==1.0.4
Expand All @@ -11,7 +11,7 @@ Jinja2==3.0.3
Mako==1.2.2
MarkupSafe==2.1.1
PyNaCl==1.5.0
SQLAlchemy-Continuum==1.3.12
SQLAlchemy-Continuum==1.3.13
SQLAlchemy-Utils==0.38.3
SQLAlchemy==1.3.24
Werkzeug==1.0.1
Expand All @@ -23,33 +23,33 @@ attrs==22.1.0
bcrypt==4.0.0
blinker==1.5
cachelib==0.9.0
certifi==2022.6.15
certifi==2022.6.15.2
cffi==1.15.1
charset-normalizer==2.1.1
click==8.1.3
croniter==1.3.7
cryptography==37.0.4
cryptography==38.0.1
dataclass-wizard==0.22.1
dpath==2.0.6
ecdsa==0.18.0
flask-jwt-oidc==0.3.0
flask-marshmallow==0.11.0
flask-restx==0.5.1
gunicorn==20.1.0
idna==3.3
idna==3.4
importlib-metadata==4.12.0
importlib-resources==5.9.0
itsdangerous==2.0.1
jaeger-client==4.8.0
jsonschema==4.15.0
jsonschema==4.16.0
marshmallow-sqlalchemy==0.25.0
marshmallow==3.17.1
minio==7.1.11
opentracing==2.4.0
packaging==21.3
paramiko==2.11.0
pkgutil_resolve_name==1.3.10
protobuf==3.19.4
protobuf==3.19.5
psycopg2-binary==2.9.3
pyasn1==0.4.8
pycparser==2.21
Expand Down
2 changes: 1 addition & 1 deletion jobs/payment-jobs/requirements/prod.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ itsdangerous==2.0.1
dataclass_wizard

-e git+https://github.com/bcgov/sbc-common-components.git#egg=sbc-common-components&subdirectory=python
-e git+https://github.com/seeker25/sbc-pay.git@paybc_fixes#egg=pay_api&subdirectory=pay-api
-e git+https://github.com/bcgov/sbc-pay.git#egg=pay_api&subdirectory=pay-api

2 changes: 2 additions & 0 deletions pay-api/src/pay_api/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,8 @@ class _Config(): # pylint: disable=too-few-public-methods

ALLOW_LEGACY_ROUTING_SLIPS = os.getenv('ALLOW_LEGACY_ROUTING_SLIPS', 'True').lower() == 'true'

ENABLE_PAYBC_AUTOMATED_REFUNDS = os.getenv('ENABLE_PAYBC_AUTOMATED_REFUNDS', 'false').lower() == 'true'

TESTING = False
DEBUG = True

Expand Down
6 changes: 3 additions & 3 deletions pay-api/src/pay_api/schemas/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from os import listdir, path
from typing import Tuple

from jsonschema import Draft7Validator, RefResolver, SchemaError, draft7_format_checker
from jsonschema import Draft7Validator, RefResolver, SchemaError


BASE_URI = 'https://bcrs.gov.bc.ca/.well_known/schemas'
Expand Down Expand Up @@ -91,14 +91,14 @@ def validate(json_data: json,
resolver = RefResolver(f'file://{schema_file_path}.json', schema, schema_store)

if Draft7Validator(schema,
format_checker=draft7_format_checker,
format_checker=Draft7Validator.FORMAT_CHECKER,
resolver=resolver
) \
.is_valid(json_data):
return True, None

errors = Draft7Validator(schema,
format_checker=draft7_format_checker,
format_checker=Draft7Validator.FORMAT_CHECKER,
resolver=resolver
) \
.iter_errors(json_data)
Expand Down
45 changes: 27 additions & 18 deletions pay-api/src/pay_api/services/direct_pay_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,14 @@
from pay_api.models import Receipt as ReceiptModel
from pay_api.models.distribution_code import DistributionCode as DistributionCodeModel
from pay_api.models.payment_line_item import PaymentLineItem as PaymentLineItemModel
from pay_api.models.payment import Payment as PaymentModel
from pay_api.services.base_payment_system import PaymentSystemService
from pay_api.services.hashing import HashingService
from pay_api.services.invoice import Invoice
from pay_api.services.invoice_reference import InvoiceReference
from pay_api.services.payment_account import PaymentAccount
from pay_api.utils.enums import (
AuthHeaderType, ContentType, InvoiceReferenceStatus, InvoiceStatus, PaymentMethod, PaymentSystem)
AuthHeaderType, ContentType, InvoiceReferenceStatus, InvoiceStatus, PaymentMethod, PaymentStatus, PaymentSystem)
from pay_api.utils.util import current_local_time, generate_transaction_number, parse_url_params

from ..exceptions import BusinessException
Expand Down Expand Up @@ -142,23 +143,31 @@ def get_pay_system_reason_code(self, pay_response_url: str) -> str: # pylint:di

def process_cfs_refund(self, invoice: InvoiceModel):
"""Process refund in CFS."""
current_app.logger.debug('<process_cfs_refund creating automated refund for invoice: '
f'{invoice.id}, {invoice.invoice_status_code}')
# No APPROVED invoices allowed for refund. Invoices typically land on PAID right away.
if invoice.invoice_status_code not in (InvoiceStatus.PAID.value, InvoiceStatus.UPDATE_REVENUE_ACCOUNT.value):
raise BusinessException(Error.INVALID_REQUEST)

refund_url = current_app.config.get('PAYBC_DIRECT_PAY_CC_REFUND_BASE_URL') + '/paybc-service/api/refund'
access_token: str = self._get_refund_token().json().get('access_token')
data = self._build_automated_refund_payload(invoice)
refund_response = self.post(refund_url, access_token, AuthHeaderType.BEARER,
ContentType.JSON, data, auth_header_name='Bearer-Token').json()
# Check if approved is 1=Success
if refund_response.get('approved') != 1:
message = 'Refund error: ' + refund_response.get('message')
current_app.logger.error(message)
raise BusinessException(Error.DIRECT_PAY_INVALID_RESPONSE)
current_app.logger.debug('>process_cfs_refund')
if current_app.config.get('ENABLE_PAYBC_AUTOMATED_REFUNDS'):
current_app.logger.debug('<process_cfs_refund creating automated refund for invoice: '
f'{invoice.id}, {invoice.invoice_status_code}')
# No APPROVED invoices allowed for refund. Invoices typically land on PAID right away.
if invoice.invoice_status_code not in \
(InvoiceStatus.PAID.value, InvoiceStatus.UPDATE_REVENUE_ACCOUNT.value):
raise BusinessException(Error.INVALID_REQUEST)

refund_url = current_app.config.get('PAYBC_DIRECT_PAY_CC_REFUND_BASE_URL') + '/paybc-service/api/refund'
access_token: str = self._get_refund_token().json().get('access_token')
data = self._build_automated_refund_payload(invoice)
refund_response = self.post(refund_url, access_token, AuthHeaderType.BEARER,
ContentType.JSON, data, auth_header_name='Bearer-Token').json()
# Check if approved is 1=Success
if refund_response.get('approved') != 1:
message = 'Refund error: ' + refund_response.get('message')
current_app.logger.error(message)
raise BusinessException(Error.DIRECT_PAY_INVALID_RESPONSE)
current_app.logger.debug('>process_cfs_refund')
else:
current_app.logger.debug(f'Processing manual refund for {invoice.id}')
super()._publish_refund_to_mailer(invoice)
payment: PaymentModel = PaymentModel.find_payment_for_invoice(invoice.id)
payment.payment_status_code = PaymentStatus.REFUNDED.value
payment.flush()

def get_receipt(self, payment_account: PaymentAccount, pay_response_url: str, invoice_reference: InvoiceReference):
"""Get the receipt details by calling PayBC web service."""
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.16.9' # pylint: disable=invalid-name
__version__ = '1.17.0' # pylint: disable=invalid-name
4 changes: 2 additions & 2 deletions pay-api/tests/docker/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ services:
image: stoplight/prism:3.3.0
command: >
mock -p 4010 --host 0.0.0.0
https://raw.githubusercontent.com/bcgov/sbc-pay/main/docs/docs/api_contract/pay-api-1.0.0.yaml
https://raw.githubusercontent.com/bcgov/sbc-pay/main/docs/docs/api_contract/pay-api-1.0.1.yaml
reports:
image: stoplight/prism:3.3.0
command: >
Expand All @@ -67,7 +67,7 @@ services:
image: stoplight/prism:3.3.0
command: >
mock -p 4010 --host 0.0.0.0
https://raw.githubusercontent.com/seeker25/sbc-pay/feature/13157/docs/docs/PayBC%20Mocking/paybc-1.0.0.yaml
https://raw.githubusercontent.com/bcgov/sbc-pay/main/docs/docs/PayBC%20Mocking/paybc-1.0.0.yaml
auth:
image: stoplight/prism:3.3.0
command: >
Expand Down
3 changes: 2 additions & 1 deletion pay-api/tests/unit/api/test_refund.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@
Test-Suite to ensure that the /receipt endpoint is working as expected.
"""

import json

from datetime import datetime
from flask import current_app

from pay_api.models import CfsAccount as CfsAccountModel
from pay_api.models import Invoice as InvoiceModel
Expand Down Expand Up @@ -259,6 +259,7 @@ def test_create_refund_with_legacy_routing_slip(session, client,

def test_create_refund_fails(session, client, jwt, app, stan_server, monkeypatch):
"""Assert that the endpoint returns 400."""
current_app.config['ENABLE_PAYBC_AUTOMATED_REFUNDS'] = True
token = jwt.create_jwt(get_claims(app_request=app), token_header)
headers = {'Authorization': f'Bearer {token}', 'content-type': 'application/json'}

Expand Down
3 changes: 3 additions & 0 deletions pay-api/tests/unit/services/test_direct_pay_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ def test_get_receipt(session, public_user_mock):

def test_process_cfs_refund_success(monkeypatch):
"""Assert refund is successful, when providing a PAID invoice, receipt, a COMPLETED invoice reference."""
current_app.config['ENABLE_PAYBC_AUTOMATED_REFUNDS'] = True
payment_account = factory_payment_account()
invoice = factory_invoice(payment_account)
invoice.invoice_status_code = InvoiceStatus.PAID.value
Expand All @@ -200,6 +201,7 @@ def test_process_cfs_refund_bad_request():
Users may only transition from PAID -> UPDATE_REVENUE_ACCOUNT.
"""
current_app.config['ENABLE_PAYBC_AUTOMATED_REFUNDS'] = True
payment_account = factory_payment_account()
invoice = factory_invoice(payment_account)
invoice.invoice_status_code = InvoiceStatus.APPROVED.value
Expand All @@ -216,6 +218,7 @@ def test_process_cfs_refund_duplicate_refund(monkeypatch):
Assert approved = 0, throws an exception.
"""
current_app.config['ENABLE_PAYBC_AUTOMATED_REFUNDS'] = True
payment_account = factory_payment_account()
invoice = factory_invoice(payment_account)
invoice.invoice_status_code = InvoiceStatus.PAID.value
Expand Down

0 comments on commit a90fe46

Please sign in to comment.