Exemple #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
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'
Exemple #3
0
            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
                            payment.payment_completion_date = datetime.utcnow()

                        elif payment_action == PaymentDAO.PaymentActions.UPGRADE.value:
                            # TODO: handle this (refund payment and prevent action?)
Exemple #4
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'))
            if payment_response.statusCode in [PaymentStatusCode.CREATED.value, PaymentStatusCode.COMPLETED.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.createdOn
                payment.payment_status_code = PaymentState.CREATED.value
                payment.payment_action = payment_action
                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,
                    '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

        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)
Exemple #5
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)