Ejemplo n.º 1
0
async def process_payment(pay_msg: dict, flask_app: Flask):
    """Render the payment status."""
    if not flask_app or not pay_msg:
        raise QueueException('Flask App or token not available.')

    with flask_app.app_context():
        logger.debug('entering process payment: %s', pay_msg)

        # capture_message(f'Queue Issue: Unable to find payment.id={payment_id} to place on email queue')
        # return

        if pay_msg.get('paymentToken', {}).get('statusCode') == PaymentState.TRANSACTION_FAILED.value:
            # TODO: The customer has cancelled out of paying, so we could note this better
            # technically the payment for this service is still pending
            logger.debug('Failed transaction on queue:%s', pay_msg)
            return

        complete_payment_status = [PaymentState.COMPLETED.value, PaymentState.APPROVED.value]
        if pay_msg.get('paymentToken', {}).get('statusCode') in complete_payment_status:
            logger.debug('COMPLETED transaction on queue: %s', pay_msg)

            if payment_token := pay_msg.get('paymentToken', {}).get('id'):
                if payment := Payment.find_by_payment_token(payment_token):

                    if update_payment := await update_payment_record(payment):
                        payment = update_payment
                    
                    await furnish_receipt_message(qsm, payment)
                    
                else:
Ejemplo n.º 2
0
async def test_update_payment_record(
        app, session, test_name, action_code, start_request_state,
        end_request_state, start_priority, end_priority, start_datetime,
        days_after_start_datetime, start_payment_state, end_payment_state,
        start_payment_date, end_payment_has_value, error):
    """Assert that the update_payment_record works as expected."""
    from namex.models import Request, State, Payment
    from namex_pay.worker import update_payment_record

    print(test_name)

    now = datetime.utcnow()

    with freeze_time(now):
        # setup
        PAYMENT_TOKEN = 'dog'
        NR_NUMBER = 'NR B000001'
        name_request = Request()
        name_request.nrNum = NR_NUMBER
        name_request.stateCd = start_request_state
        name_request._source = 'NRO'
        name_request.expirationDate = start_datetime
        name_request.priorityCd = start_priority
        name_request.save_to_db()

        payment = Payment()
        payment.nrId = name_request.id
        payment._payment_token = PAYMENT_TOKEN
        payment._payment_status_code = start_payment_state
        payment.payment_action = action_code
        payment.furnished = False
        payment._payment_completion_date = start_payment_date
        payment.save_to_db()

        # run test
        if error:  # expecting it to raise an error
            with pytest.raises(error):
                await update_payment_record(payment)
        else:
            # else it was processable
            if not (payment_final := await update_payment_record(payment)):
                payment_final = payment

            nr_final = Request.find_by_nr(NR_NUMBER)

            assert nr_final.stateCd == end_request_state
            assert nr_final.priorityCd == end_priority
            assert nr_final.expirationDate == (
                start_datetime
                or now) + timedelta(days=days_after_start_datetime)
            assert eval(
                f'payment_final.payment_completion_date {end_payment_has_value} None'
            )
            assert payment_final.payment_status_code == end_payment_state
Ejemplo n.º 3
0
    def delete(self, nr_id, payment_id):
        try:
            # Find the existing name request
            nr_model = RequestDAO.query.get(nr_id)
            if not nr_model:
                return jsonify(message=f'No NR found with id: {nr_id}.'), 404

            # Find the existing payment record
            payment = PaymentDAO.find_by_payment_token(payment_id)
            if not payment:
                return jsonify(message=f'No payment record with id: {payment_id}.'), 404

            # check payment record state is CREATED
            current_payment_state = payment.payment_status_code
            if current_payment_state != PaymentStatusCode.CREATED.value:
                return jsonify(message=f'Unable to cancel a payment record in {current_payment_state} state.'), 400

            try:
                # cancelling may change with refactor
                cancel_payment(payment.payment_token)

                payment.payment_status_code = PaymentState.CANCELLED.value
                payment.save_to_db()

                response_data = nr_model.json()
                # Add the list of valid Name Request actions for the given state to the response
                response_data['actions'] = get_nr_state_actions(nr_model.stateCd, nr_model)
                return jsonify(response_data), 200

            except PaymentServiceError as err:
                # should only get here if there was a conflict (payment went through before cancel happened)
                return handle_exception(err, err.message, 409)
        except Exception as err:
            return handle_exception(err, repr(err), 500)
Ejemplo n.º 4
0
    def post(nr_num):
        try:
            # TODO: Validate NR string format
            # if not RequestDAO.validNRFormat(nr_num):
            #    return jsonify(message='NR number is not in a valid format \'NR 9999999\''), 400

            nr_draft = RequestDAO.find_by_nr(nr_num)
            if not nr_draft:
                # Should this be a 400 or 404... hmmm
                return jsonify(message='{nr_num} not found'.format(nr_num=nr_num)), 400

            json_input = request.get_json()
            if not json_input:
                return jsonify(message=MSG_BAD_REQUEST_NO_JSON_BODY), 400
            elif isinstance(json_input, str):
                json_input = json.loads(json_input)

            # Grab the info we need off the request
            payment_info = json_input.get('paymentInfo')
            filing_info = json_input.get('filingInfo')
            business_info = json_input.get('businessInfo')

            # Create our payment request
            req = PaymentRequest(
                payment_info=payment_info,
                filing_info=filing_info,
                business_info=business_info
            )

            payment_response = create_payment(req)
            if not payment_response:
                raise PaymentServiceError(message=MSG_ERROR_CREATING_RESOURCE)

            if payment_response and payment_response.status_code == PaymentStatusCode.CREATED.value:
                # Save the payment info to Postgres
                payment = PaymentDAO()
                payment.nrId = nr_draft.id
                payment.payment_token = str(payment_response.id)
                payment.payment_completion_date = payment_response.created_on
                payment.payment_status_code = PaymentState.CREATED.value
                payment.save_to_db()

                data = jsonify(payment_response.to_dict())
                response = make_response(data, 200)
                return response

        except PaymentServiceError as err:
            return handle_exception(err, err.message, 500)
        except SBCPaymentException as err:
            return handle_exception(err, err.message, 500)
        except SBCPaymentError as err:
            return handle_exception(err, err.message, 500)
        except Exception as err:
            return handle_exception(err, err, 500)
Ejemplo n.º 5
0
async def furnish_receipt_message(qsm: QueueServiceManager, payment: Payment):  # pylint: disable=redefined-outer-name
    """Send receipt info to the mail queue if it hasn't yet been done."""
    if payment.furnished == 'Y':
        logger.debug(
            'Queue Issue: Duplicate, already furnished receipt for payment.id=%s',
            payment.id)
        capture_message(
            f'Queue Issue: Duplicate, already furnished receipt for payment.id={payment.id}'
        )
        return

    nr = None

    logger.debug('Start of the furnishing of receipt for payment record:%s',
                 payment.as_dict())
    try:
        payment.furnished = True
        payment.save_to_db()
    except Exception as err:  # noqa: B902; bare exception to catch all
        raise Exception('Unable to alter payment record.') from err

    try:
        nr = RequestDAO.find_by_id(payment.nrId)
        cloud_event_msg = create_cloud_event_msg(
            msg_id=str(uuid.uuid4()),
            msg_type='bc.registry.names.request',
            source=f'/requests/{nr.nrNum}',
            time=datetime.utcfromtimestamp(
                time.time()).replace(tzinfo=timezone.utc).isoformat(),
            identifier=nr.nrNum,
            json_data_body={
                'request': {
                    'header': {
                        'nrNum': nr.nrNum
                    },
                    'paymentToken': payment.payment_token,
                    'statusCode': nr.stateCd
                }
            })
        logger.debug('About to publish email for payment.id=%s', payment.id)
        await publish_email_message(qsm, cloud_event_msg)
    except Exception as err:  # noqa: B902; bare exception to catch all
        payment.furnished = False
        payment.save_to_db()
        logger.debug('Reset payment furnish status payment.id= %s', payment.id)
        raise QueueException(f'Unable to furnish NR info. {err}') from err
Ejemplo n.º 6
0
async def test_process_payment(
    app,
    session,
    mocker,
    test_name,
    action_code,
    start_request_state,
    start_priority,
    start_datetime,
    start_payment_state,
    start_payment_date,
):
    from namex.models import Request, State, Payment
    from namex_pay.worker import process_payment, FLASK_APP
    nest_asyncio.apply()

    # setup
    PAYMENT_TOKEN = 'dog'
    NR_NUMBER = 'NR B000001'
    name_request = Request()
    name_request.nrNum = NR_NUMBER
    name_request.stateCd = start_request_state
    name_request._source = 'NRO'
    name_request.expirationDate = start_datetime
    name_request.priorityCd = start_priority
    name_request.save_to_db()

    payment = Payment()
    payment.nrId = name_request.id
    payment._payment_token = PAYMENT_TOKEN
    payment._payment_status_code = start_payment_state
    payment.payment_action = action_code
    payment.furnished = False
    payment._payment_completion_date = start_payment_date
    payment.save_to_db()

    # setup mock and patch
    msg = None

    def catch(qsm, cloud_event_msg):
        nonlocal msg
        msg = cloud_event_msg

    mocker.patch('namex_pay.worker.publish_email_message', side_effect=catch)

    # Test
    pay_msg = {
        "paymentToken": {
            "id": PAYMENT_TOKEN,
            "statusCode": "COMPLETED",
            "filingIdentifier": None
        }
    }
    await process_payment(pay_msg, FLASK_APP)

    print(msg)
    # Verify message that would be sent to the email server
    assert msg['type'] == 'bc.registry.names.request'
    assert msg['source'] == '/requests/NR B000001'
    assert msg['datacontenttype'] == 'application/json'
    assert msg['identifier'] == 'NR B000001'
    assert msg['data']['request']['header']['nrNum'] == NR_NUMBER
    assert msg['data']['request']['paymentToken'] == PAYMENT_TOKEN
    assert msg['data']['request']['statusCode'] == 'DRAFT'
Ejemplo n.º 7
0
    def post(self, nr_id, payment_action=NameRequestActions.COMPLETE.value):
        """
        At this point, the Name Request will still be using a TEMPORARY NR number.
        Confirming the payment on the frontend triggers this endpoint. Here, we:
        - Save the request to NRO which gives us a real NR.
        - Create the payment via SBC Pay.
        - If payment creation is successful, create a corresponding payment record in our system.
        :param nr_id:
        :param payment_action:
        :return:
        """
        try:
            # Find the existing name request
            nr_model = RequestDAO.query.get(nr_id)

            if not nr_model:
                # Should this be a 400 or 404... hmmm
                return jsonify(message='Name Request {nr_id} not found'.format(
                    nr_id=nr_id)), 400

            if not payment_action:
                return jsonify(
                    message='Invalid payment action, {action} not found'.
                    format(action=payment_action)), 400

            valid_payment_action = payment_action in [
                NameRequestActions.COMPLETE.value,
                NameRequestActions.UPGRADE.value,
                NameRequestActions.REAPPLY.value
            ]

            if not valid_payment_action:
                return jsonify(
                    message='Invalid payment action [{action}]'.format(
                        action=payment_action)), 400

            # We only handle payments if the NR is in the following states
            valid_payment_states = [
                State.DRAFT, State.COND_RESERVE, State.RESERVED,
                State.CONDITIONAL, State.APPROVED
            ]
            valid_nr_state = nr_model.stateCd in valid_payment_states
            if not valid_nr_state:
                return jsonify(message='Invalid NR state'.format(
                    action=payment_action)), 400

            if valid_payment_action and valid_nr_state:
                if payment_action in [NameRequestActions.COMPLETE.value]:
                    # Save the record to NRO, which swaps the NR-L Number for a real NR
                    update_solr = True
                    nr_model = self.add_records_to_network_services(
                        nr_model, update_solr)

            json_input = request.get_json()
            payment_request = {}
            if not json_input:
                # return jsonify(message=MSG_BAD_REQUEST_NO_JSON_BODY), 400
                # Grab the data from the NR, if it exists
                payment_request = build_payment_request(nr_model)
            elif isinstance(json_input, dict):
                payment_request = merge_payment_request(nr_model, json_input)
            elif isinstance(json_input, str):
                payment_request = merge_payment_request(
                    nr_model, json.loads(json_input))

            # Grab the info we need off the request
            payment_info = payment_request.get('paymentInfo')
            filing_info = payment_request.get('filingInfo')
            business_info = payment_request.get('businessInfo')

            # Create our payment request
            req = PaymentRequest(payment_info=payment_info,
                                 filing_info=filing_info,
                                 business_info=business_info)

            payment_response = create_payment(req)

            if not payment_response:
                raise PaymentServiceError(message=MSG_ERROR_CREATING_RESOURCE)

            if payment_response and payment_response.status_code == PaymentStatusCode.CREATED.value:
                # Save the payment info to Postgres
                payment = PaymentDAO()
                payment.nrId = nr_model.id
                payment.payment_token = str(payment_response.id)
                payment.payment_completion_date = payment_response.created_on
                payment.payment_status_code = PaymentState.CREATED.value
                payment.save_to_db()

                # Wrap the response, providing info from both the SBC Pay response and the payment we created
                data = jsonify({
                    'id': payment.id,
                    'nrId': payment.nrId,
                    'token': payment.payment_token,
                    'statusCode': payment.payment_status_code,
                    'completionDate': payment.payment_completion_date,
                    'payment': payment.as_dict(),
                    'sbcPayment': payment_response.to_dict()
                })

                # Record the event
                nr_svc = self.nr_service
                # EventRecorder.record(nr_svc.user, Event.PATCH + ' [payment ID: {id}]'.format(id=payment.id), nr_model, data)

                response = make_response(data, 201)
                return response

        except PaymentServiceError as err:
            return handle_exception(err, err.message, 500)
        except SBCPaymentException as err:
            return handle_exception(err, err.message, err.status_code)
        except SBCPaymentError as err:
            return handle_exception(err, err.message, 500)
        except Exception as err:
            return handle_exception(err, err, 500)
Ejemplo n.º 8
0
async def test_furnish_receipt_message(app, session, stan_server, event_loop,
                                       client_id, entity_stan, future):
    """Assert that events are placed on the email queue and the payment is marked furnished."""
    from queue_common.messages import create_cloud_event_msg
    from queue_common.service import ServiceWorker
    from queue_common.service_utils import subscribe_to_queue
    from namex_pay.worker import APP_CONFIG, furnish_receipt_message, qsm
    from namex.models import Request, State, Payment

    print('test vars')
    print(app, session, stan_server, event_loop, client_id, entity_stan,
          future)
    # setup
    PAYMENT_TOKEN = 'dog'
    NR_NUMBER = 'NR B000001'
    name_request = Request()
    name_request.nrNum = NR_NUMBER
    name_request.stateCd = State.DRAFT
    name_request._source = 'NRO'
    name_request.save_to_db()

    payment = Payment()
    payment.nrId = name_request.id
    payment._payment_token = PAYMENT_TOKEN
    payment._payment_status_code = 'COMPLETED'
    payment.furnished = False
    payment.save_to_db()

    # file handler callback
    msgs = []
    s = ServiceWorker()
    s.sc = entity_stan
    qsm.service = s

    async def cb_handler(msg):
        nonlocal msgs
        nonlocal future
        msgs.append(msg)
        print('call back recvd')
        if len(msgs) == 1:
            future.set_result(True)

    file_handler_subject = APP_CONFIG.EMAIL_PUBLISH_OPTIONS['subject']
    print(f'file_handler_subject:{file_handler_subject}')

    await subscribe_to_queue(entity_stan, file_handler_subject,
                             f'entity_queue.{file_handler_subject}',
                             f'entity_durable_name.{file_handler_subject}',
                             cb_handler)

    print(payment.as_dict())
    # sanity check
    assert name_request.id
    assert payment.nrId == name_request.id

    # test
    await furnish_receipt_message(qsm, payment)

    try:
        await asyncio.wait_for(future, 1, loop=event_loop)
    except Exception as err:
        print(err)

    # results
    processed_payment = Payment.find_by_payment_token(PAYMENT_TOKEN)

    # verify
    assert processed_payment.furnished
    assert len(msgs) == 1
    cloud_event = json.loads(msgs[0].data.decode('utf-8'))
    assert cloud_event['identifier'] == NR_NUMBER
Ejemplo n.º 9
0
async def update_payment_record(payment: Payment) -> Optional[Payment]:
    """Update the payment record in the database.

    Alter the NR state as required based on the payment action.
    Payment NR Action - One of [COMPLETE, UPGRADE, REAPPLY]
    COMPLETE - set NR to DRAFT IFF nr.state == PENDING_PAYMENT
    UPGRADE - set the nr.priority to True/'Y'
    REAPPLY - add REAPPLY_EXTENSION to expiry date of NR IFF it hasn't expired
    """
    if payment.payment_completion_date:
        msg = f'Queue Issue: Duplicate, payment already processed for payment.id={payment.id}'
        logger.debug(msg)
        capture_message(msg)
        return None

    payment_action = payment.payment_action
    nr = RequestDAO.find_by_id(payment.nrId)
    if payment_action == Payment.PaymentActions.CREATE.value:  # pylint: disable=R1705
        if nr.stateCd == State.PENDING_PAYMENT:
            nr.stateCd = State.DRAFT
            payment.payment_completion_date = datetime.utcnow()
            payment.payment_status_code = State.COMPLETED

            nr.save_to_db()
            payment.save_to_db()
        return payment

    elif payment_action == Payment.PaymentActions.UPGRADE.value:
        if nr.stateCd == State.PENDING_PAYMENT:
            msg = f'Queue Issue: Upgrading a non-DRAFT NR for payment.id={payment.id}'
            logger.debug(msg)
            capture_message(msg)
            raise QueueException(msg)

        nr.priorityCd = 'Y'
        nr.priorityDate = datetime.utcnow()
        payment.payment_completion_date = datetime.utcnow()
        payment.payment_status_code = State.COMPLETED

        nr.save_to_db()
        payment.save_to_db()
        return payment

    elif payment_action == Payment.PaymentActions.REAPPLY.value:
        if nr.stateCd != State.APPROVED \
                and nr.expirationDate + timedelta(hours=NAME_REQUEST_EXTENSION_PAD_HOURS) < datetime.utcnow():
            msg = f'Queue Issue: Failed attempt to extend NR for payment.id={payment.id} '\
                'nr.state{nr.stateCd}, nr.expires:{nr.expirationDate}'
            logger.debug(msg)
            capture_message(msg)
            raise QueueException(msg)
        nr.expirationDate = nr.expirationDate + timedelta(days=NAME_REQUEST_LIFESPAN_DAYS)
        payment.payment_completion_date = datetime.utcnow()
        payment.payment_status_code = State.COMPLETED

        nr.save_to_db()
        payment.save_to_db()
        return payment

    msg = f'Queue Issue: Unknown action:{payment_action} for payment.id={payment.id}'
    logger.debug(msg)
    capture_message(msg)
    raise QueueException(f'Unknown action:{payment_action} for payment.id={payment.id}')
Ejemplo n.º 10
0
            req = PaymentRequest(paymentInfo=payment_info,
                                 filingInfo=filing_info,
                                 businessInfo=business_info,
                                 accountInfo=account_info,
                                 details=details)

            payment_response = create_payment(req.as_dict(), headers)
            try:
                successful_status_list = [
                    PaymentStatusCode.APPROVED.value,
                    PaymentStatusCode.CREATED.value,
                    PaymentStatusCode.COMPLETED.value
                ]
                if payment_response.statusCode in successful_status_list:
                    # Save the payment info to Postgres
                    payment = PaymentDAO()
                    payment.nrId = nr_model.id
                    payment.payment_token = str(payment_response.id)
                    # namex-pay will set payment_status_code to completed state after actioning it on the queue
                    payment.payment_status_code = payment_response.statusCode
                    payment.payment_action = payment_action
                    payment.save_to_db()

                    # happens for PAD. If completed/approved right away queue will have err'd so apply changes here
                    # TODO: send email / furnish payment for these
                    if payment_response.statusCode in [
                            PaymentStatusCode.APPROVED.value,
                            PaymentStatusCode.COMPLETED.value
                    ]:
                        if payment_action == PaymentDAO.PaymentActions.CREATE.value:  # pylint: disable=R1705
                            if nr_model.stateCd == State.PENDING_PAYMENT:
Ejemplo n.º 11
0
async def process_payment(pay_msg: dict, flask_app: Flask):
    """Render the payment status."""
    if not flask_app or not pay_msg:
        raise QueueException('Flask App or token not available.')

    with flask_app.app_context():
        logger.debug('entering process payment: %s', pay_msg)

        # capture_message(f'Queue Issue: Unable to find payment.id={payment_id} to place on email queue')
        # return

        if pay_msg.get(
                'paymentToken',
            {}).get('statusCode') == PaymentState.TRANSACTION_FAILED.value:
            # TODO: The customer has cancelled out of paying, so we could note this better
            # technically the payment for this service is still pending
            logger.debug('Failed transaction on queue:%s', pay_msg)
            return

        complete_payment_status = [
            PaymentState.COMPLETED.value, PaymentState.APPROVED.value
        ]
        if pay_msg.get('paymentToken',
                       {}).get('statusCode') in complete_payment_status:  # pylint: disable=R1702
            logger.debug('COMPLETED transaction on queue: %s', pay_msg)

            if payment_token := pay_msg.get('paymentToken', {}).get('id'):
                if payment := Payment.find_by_payment_token(payment_token):

                    if update_payment := await update_payment_record(payment):
                        payment = update_payment
                        # record event
                        nr = RequestDAO.find_by_id(payment.nrId)
                        # TODO: create a namex_pay user for this
                        user = User.find_by_username(
                            'name_request_service_account')
                        EventRecorder.record(
                            user, Event.NAMEX_PAY +
                            f' [payment completed] { payment.payment_action }',
                            nr, nr.json())
                        # try to update NRO otherwise send a sentry msg for OPS
                        if payment.payment_action in \
                                [payment.PaymentActions.UPGRADE.value, payment.PaymentActions.REAPPLY.value]:
                            change_flags = {
                                'is_changed__request': True,
                                'is_changed__previous_request': False,
                                'is_changed__applicant': False,
                                'is_changed__address': False,
                                'is_changed__name1': False,
                                'is_changed__name2': False,
                                'is_changed__name3': False,
                                'is_changed__nwpta_ab': False,
                                'is_changed__nwpta_sk': False,
                                'is_changed__request_state': False,
                                'is_changed_consent': False
                            }
                            warnings = nro.change_nr(nr, change_flags)
                            if warnings:
                                logger.error(
                                    'Queue Error: Unable to update NRO :%s',
                                    warnings)
                                capture_message(
                                    f'Queue Error: Unable to update NRO for {nr} {payment.payment_action} :{warnings}',
                                    level='error')

                    await furnish_receipt_message(qsm, payment)

                else:
Ejemplo n.º 12
0
    def post(self, nr_id, payment_action=NameRequestActions.CREATE.value):
        """
        At this point, the Name Request will still be using a TEMPORARY NR number.
        Confirming the payment on the frontend triggers this endpoint. Here, we:
        - Save the request to NRO which gives us a real NR.
        - Create the payment via SBC Pay.
        - If payment creation is successful, create a corresponding payment record in our system.
        :param nr_id:
        :param payment_action:
        :return:
        """
        try:
            # Find the existing name request
            nr_model = RequestDAO.query.get(nr_id)

            if not nr_model:
                # Should this be a 400 or 404... hmmm
                return jsonify(message='Name Request {nr_id} not found'.format(nr_id=nr_id)), 400

            if not payment_action:
                return jsonify(message='Invalid payment action, {action} not found'.format(action=payment_action)), 400

            valid_payment_action = payment_action in [
                NameRequestActions.CREATE.value,
                NameRequestActions.UPGRADE.value,
                NameRequestActions.REAPPLY.value
            ]

            if not valid_payment_action:
                return jsonify(message='Invalid payment action [{action}]'.format(action=payment_action)), 400

            # We only handle payments if the NR is in the following states
            valid_payment_states = [State.DRAFT, State.COND_RESERVE, State.RESERVED, State.CONDITIONAL, State.APPROVED,
                                    State.PENDING_PAYMENT]
            valid_nr_state = nr_model.stateCd in valid_payment_states
            if not valid_nr_state:
                return jsonify(message='Invalid NR state'.format(action=payment_action)), 400

            if valid_payment_action and valid_nr_state:
                if payment_action in [NameRequestActions.CREATE.value]:
                    # Save the record to NRO, which swaps the NR-L Number for a real NR
                    update_solr = True
                    nr_model = self.add_records_to_network_services(nr_model, update_solr)

            json_input = request.get_json()
            payment_request = {}
            if not json_input:
                # return jsonify(message=MSG_BAD_REQUEST_NO_JSON_BODY), 400
                # Grab the data from the NR, if it exists
                payment_request = build_payment_request(nr_model)
            elif isinstance(json_input, dict):
                payment_request = merge_payment_request(nr_model, json_input)
            elif isinstance(json_input, str):
                payment_request = merge_payment_request(nr_model, json.loads(json_input))

            # Grab the info we need off the request
            payment_info = payment_request.get('paymentInfo', {})
            filing_info = payment_request.get('filingInfo')
            business_info = payment_request.get('businessInfo')

            # Create our payment request
            req = PaymentRequest(
                paymentInfo=payment_info,
                filingInfo=filing_info,
                businessInfo=business_info
            )
            payment_response = create_payment(req.as_dict(), json_input.get('headers'))
            try:
                successful_status_list = [
                    PaymentStatusCode.APPROVED.value,
                    PaymentStatusCode.CREATED.value,
                    PaymentStatusCode.COMPLETED.value
                ]
                if payment_response.statusCode in successful_status_list:
                    # Save the payment info to Postgres
                    payment = PaymentDAO()
                    payment.nrId = nr_model.id
                    payment.payment_token = str(payment_response.id)
                    # namex-pay will set payment_status_code to completed state after actioning it on the queue
                    payment.payment_status_code = payment_response.statusCode
                    payment.payment_action = payment_action
                    payment.save_to_db()

                    # happens for PAD. If completed/approved right away queue will have err'd so apply changes here
                    # TODO: send email / furnish payment for these
                    if payment_response.statusCode in [PaymentStatusCode.APPROVED.value, PaymentStatusCode.COMPLETED.value]:
                        if payment_action == PaymentDAO.PaymentActions.CREATE.value:  # pylint: disable=R1705
                            if nr_model.stateCd == State.PENDING_PAYMENT:
                                nr_model.stateCd = State.DRAFT
                            payment.payment_completion_date = datetime.utcnow()

                        elif payment_action == PaymentDAO.PaymentActions.UPGRADE.value:
                            # TODO: handle this (refund payment and prevent action?)
                            if nr_model.stateCd == State.PENDING_PAYMENT:
                                msg = f'Upgrading a non-DRAFT NR for payment.id={payment.id}'
                                current_app.logger.debug(msg)

                            nr_model.priorityCd = 'Y'
                            nr_model.priorityDate = datetime.utcnow()
                            payment.payment_completion_date = datetime.utcnow()

                        elif payment_action == PaymentDAO.PaymentActions.REAPPLY.value:
                            # TODO: handle this (refund payment and prevent action?)
                            if nr_model.stateCd != State.APPROVED \
                                    and nr_model.expirationDate + timedelta(hours=NAME_REQUEST_EXTENSION_PAD_HOURS) < datetime.utcnow():
                                msg = f'Extend NR for payment.id={payment.id} nr_model.state{nr_model.stateCd}, nr_model.expires:{nr_model.expirationDate}'
                                current_app.logger.debug(msg)

                            nr_model.expirationDate = nr_model.expirationDate + timedelta(days=NAME_REQUEST_LIFESPAN_DAYS)
                            payment.payment_completion_date = datetime.utcnow()
                        
                        nr_model.save_to_db()
                        payment.save_to_db()

                    # Wrap the response, providing info from both the SBC Pay response and the payment we created
                    data = jsonify({
                        'id': payment.id,
                        'nrId': payment.nrId,
                        'nrNum': nr_model.nrNum,
                        'token': payment.payment_token,
                        'statusCode': payment.payment_status_code,
                        'action': payment.payment_action,
                        'completionDate': payment.payment_completion_date,
                        'payment': payment.as_dict(),
                        'sbcPayment': payment_response.as_dict()
                    })

                    # Record the event
                    # nr_svc = self.nr_service
                    # EventRecorder.record(nr_svc.user, Event.POST + ' [payment created]', json_input)

                    response = make_response(data, 201)
                    return response
                # something went wrong with status code above
                else:
                    # log actual status code
                    current_app.logger.debug('Error with status code. Actual status code: ' + payment_response.statusCode)
                    # return generic error status to the front end
                    return jsonify(message='Name Request {nr_id} encountered an error'.format(nr_id=nr_id)), 402
            except Exception as err:
                current_app.logger.error(err.with_traceback(None))
                return jsonify(message='Name Request {nr_id} encountered an error'.format(nr_id=nr_id)), 500

        except PaymentServiceError as err:
            return handle_exception(err, err.message, 500)
        except SBCPaymentException as err:
            return handle_exception(err, err.message, err.status_code)
        except SBCPaymentError as err:
            return handle_exception(err, err.message, 500)
        except Exception as err:
            return handle_exception(err, err, 500)