def request_refund(self, nr_model: RequestDAO, payment_id: int): """ Processes a SINGLE refund request. This is different from the 'refund' in the NameRequest resource PATCH namerequests/{nrId}/REQUEST_REFUND which cancels the NR and refunds any associated payments. :param nr_model: :param payment_id: :return: """ # Handle the payments valid_states = [ PaymentState.COMPLETED.value, PaymentState.PARTIAL.value ] if nr_model.stateCd not in [State.DRAFT]: raise PaymentServiceError(message='Invalid NR state for cancel and refund') # Cancel any payments associated with the NR for payment in nr_model.payments.all(): if payment.payment_status_code in valid_states and payment.id == payment_id: # refund_payment(payment.payment_token, {'reason': 'Name Request user requested refund'}) refund_payment(payment.payment_token, {}) payment.payment_status_code = PaymentState.REFUND_REQUESTED.value payment.save_to_db() return nr_model
def complete_reapply_payment(self, nr_model: RequestDAO, payment_id: int): """ Invoked when re-applying for an existing Name Request reservation. Extend the Name Request's expiration date by 56 days. If the request action is set to REH or REST, extend the expiration by an additional year (plus the default 56 days). :param nr_model: :param payment_id: :return: """ nr_svc = self.nr_service # Update the state of the payment payment = get_active_payment(nr_model, payment_id) sbc_payment_response = get_payment(payment.payment_token) # TODO: Throw errors if this fails! if sbc_payment_response.statusCode in [ PaymentStatusCode.COMPLETED.value, PaymentStatusCode.APPROVED.value ]: payment.payment_status_code = sbc_payment_response.statusCode payment.payment_completion_date = sbc_payment_response.createdOn payment.save_to_db() if nr_model.submitCount < 3: if nr_model.request_action_cd in [ RequestAction.REH.value, RequestAction.REN.value ]: # If request action is REH or REST extend by 1 year (+ 56 default) days, starting tomorrow nr_model = nr_svc.extend_expiry_date(nr_model, datetime.utcnow(), days=421) nr_model = nr_svc.update_request_submit_count(nr_model) else: # Extend expiry date by (default) 56 days, starting tomorrow nr_model = nr_svc.extend_expiry_date(nr_model, datetime.utcnow(), days=56) nr_model = nr_svc.update_request_submit_count(nr_model) nr_model.save_to_db() else: # TODO: Make a custom exception for this? raise PaymentServiceError( message= 'Submit count maximum of 3 retries has been reached!') # This (optionally) handles the updates for NRO and Solr, if necessary update_solr = False nr_model = self.update_records_in_network_services( nr_model, update_solr) # Update the actions, as things change once the payment is successful self.nr_service.current_state_actions = get_nr_state_actions( nr_model.stateCd, nr_model) # Record the event # EventRecorder.record(nr_svc.user, Event.PATCH + ' [re-apply]', nr_model, nr_svc.request_data) return nr_model
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)
def cancel_payment(self, nr_model: RequestDAO, payment_id: int): # Cancel payment with specified id. valid_states = [ PaymentState.CREATED.value ] for payment in nr_model.payments.all(): if payment.id == payment_id and payment.payment_status_code in valid_states: sbc_payment_response = get_payment(payment.payment_token) if sbc_payment_response.statusCode in [PaymentStatusCode.COMPLETED.value]: raise PaymentServiceError(message='Error cancelling payment. Payment is in a completed state!') cancel_payment(payment.payment_token) payment.payment_status_code = PaymentState.CANCELLED.value payment.save_to_db() return nr_model
def complete_upgrade_payment(self, nr_model: RequestDAO, payment_id: int): """ Invoked when upgrading an existing Name Request reservation to PRIORITY status. :param nr_model: :param payment_id: :return: """ nr_svc = self.nr_service if nr_model.stateCd not in [State.DRAFT, State.PENDING_PAYMENT]: raise PaymentServiceError( message= 'Error upgrading Name Request, request is in an invalid state!' ) # Update the state of the payment payment = get_active_payment(nr_model, payment_id) sbc_payment_response = get_payment(payment.payment_token) # TODO: Throw errors if this fails! if sbc_payment_response.statusCode in [ PaymentStatusCode.COMPLETED.value, PaymentStatusCode.APPROVED.value ]: payment.payment_status_code = sbc_payment_response.statusCode payment.payment_completion_date = sbc_payment_response.createdOn payment.save_to_db() nr_model.priorityCd = 'Y' nr_model.priorityDate = datetime.utcnow() # Save the name request nr_model.save_to_db() # This (optionally) handles the updates for NRO and Solr, if necessary update_solr = False nr_model = self.update_records_in_network_services( nr_model, update_solr) # Update the actions, as things change once the payment is successful self.nr_service.current_state_actions = get_nr_state_actions( nr_model.stateCd, nr_model) # Record the event EventRecorder.record(nr_svc.user, Event.PATCH + ' [payment completed] UPGRADE', nr_model, nr_model.json()) return nr_model
def put(self, nr_id, payment_id): 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='{nr_id} not found'.format( nr_id=nr_id)), 400 payment_id = clean_url_path_param(payment_id) json_input = request.get_json() if not json_input: return jsonify(message=MSG_BAD_REQUEST_NO_JSON_BODY), 400 # 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') # Update our payment request req = PaymentRequest(payment_info=payment_info, filing_info=filing_info, business_info=business_info) payment_response = update_payment(payment_id, req) if not payment_response: raise PaymentServiceError(message=MSG_ERROR_CREATING_RESOURCE) 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, err.status_code) except SBCPaymentError as err: return handle_exception(err, err.message, 500) except Exception as err: return handle_exception(err, err, 500)
def cancel_payment(self, nr_model: RequestDAO, payment_id: int): # Cancel payment with specified id. valid_states = [PaymentState.CREATED.value] for payment in nr_model.payments.all(): if payment.id == payment_id and payment.payment_status_code in valid_states: sbc_payment_response = get_payment(payment.payment_token) if sbc_payment_response.statusCode in [ PaymentStatusCode.COMPLETED.value, PaymentStatusCode.APPROVED.value ]: raise PaymentServiceError( message= 'Error cancelling payment. Payment is in a completed state!' ) cancel_payment(payment.payment_token) payment.payment_status_code = PaymentState.CANCELLED.value payment.save_to_db() # record the event nr_svc = self.nr_service EventRecorder.record( nr_svc.user, Event.DELETE + f' [payment cancelled] {payment.payment_action}', nr_model, nr_model.json()) return nr_model
def put(payment_identifier): try: payment_identifier = clean_url_path_param(payment_identifier) json_input = request.get_json() if not json_input: return jsonify(message=MSG_BAD_REQUEST_NO_JSON_BODY), 400 # 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') # Update our payment request req = PaymentRequest( payment_info=payment_info, filing_info=filing_info, business_info=business_info ) payment_response = update_payment(payment_identifier, req) if not payment_response: raise PaymentServiceError(message=MSG_ERROR_CREATING_RESOURCE) 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)
def patch(self, nr_id, payment_id, payment_action): """ :param nr_id: :param payment_id: :param payment_action: :return: """ try: # Find the existing name request nr_model = RequestDAO.query.get(nr_id) # Creates a new NameRequestService, validates the app config, and sets request_data to the NameRequestService instance # Override the default self.initialize method def initialize(_self): # The request payload will be empty when making this call, # but we still want to process names, so we need to add # them to the request, otherwise they won't be processed! _self.request_data = { 'names': [n.as_dict() for n in nr_model.names.all()] } # Set the request data to the service _self.nr_service.request_data = self.request_data initialize(self) nr_svc = self.nr_service nr_svc.nr_num = nr_model.nrNum nr_svc.nr_id = nr_model.id valid_update_states = [ State.DRAFT, State.COND_RESERVE, State.RESERVED ] # This could be moved out, but it's fine here for now def validate_patch_request(nr): is_valid = True msg = '' if nr.stateCd in valid_update_states: is_valid = True return is_valid, msg is_valid_patch, validation_msg = validate_patch_request(nr_model) validation_msg = validation_msg if not len( validation_msg) > 0 else 'Invalid request for PATCH' if not is_valid_patch: raise PaymentServiceError(message=validation_msg) process_payment = has_active_payment(nr_model, payment_id) if nr_model.stateCd in valid_update_states and not process_payment: pass elif process_payment: # This handles updates if the NR state is 'patchable' nr_model = self.handle_payment_actions(payment_action, nr_model, payment_id) current_app.logger.debug(nr_model.json()) response_data = nr_model.json() # Add the list of valid Name Request actions for the given state to the response response_data['actions'] = nr_svc.current_state_actions return jsonify(response_data), 200 except PaymentServiceError as err: return handle_exception(err, err.message, 500) except Exception as err: return handle_exception(err, repr(err), 500)
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)