Example #1
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
Example #2
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)
Example #3
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'
Example #4
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)
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
Example #6
0
                                 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:
                                nr_model.stateCd = State.DRAFT
Example #7
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)