Transaction Origination
This section walks through ZipRights initial user-interface rendering workflow.
Determine: New Transaction or Existing Transaction?
As part of its user-interface bootstrap process - the first determination the ZipRight UI needs to make, is whether it is being launched in the context of a new transaction, or to view details on an existing transaction. ZipRight makes this determination as part of its initial page load - by retrieving the origination context for the user-interaction from the JavaScript API's transaction.getOrigin
method.
If the origination context contains a valid transactionId
, indicating the UI has been launched in the context of an existing transaction - ZipRight routes to its order details page.
Else - it routes to its new order page:
...
// Function to instantiate application state with origination context
const setOriginationContext = async () => {
// Instantiate instance of host application interface
// and invoke access to origination context
const proxy = await host;
const originationData = await proxy.getTransactionOrigin();
console.log(originationData);
// Access and instantiate application state with originating context
const currentTransactionId = originationData.transactionId;
if (currentTransactionId) {
// If in context of existing transaction - navigate to details view
navigate(`/details/${currentTransactionId}`);
} else {
// Else - navigate to new order view
navigate('/order');
}
};
...
Case: New Transaction
For a new transaction, ZipRight takes the user through a wizard experience where they are able to view and verify the borrower, co-borrower, subject property and mortgage transaction information.
ZipRight accesses this information via the REST API's GET /partner/v2/origins/:id
endpoint, which requires a valid partnerAccessToken
and originId
, also received from the origination context as illustrated above. The ZipRight UI passes this information to the ZipRight back-end when invoking the back-ends GET /origins/:id?partnerAccessToken={{token}}
endpoint, which in turn calls the EPC REST API's origins endpoint to retrieve and marshall this information back up to the user-interface.
Don't confuse the ZipRight back-end's
GET /origins/:id?partnerAccessToken={{token}}
endpoint with the EPC REST API'sGET /partner/v2/origins/:id
endpoint. The former is a proxy endpoint for the user-interface to retrieve information accessible via the latter.If you're wondering why ZipRight jumps a hop to access this information - keep in mind that the EPC
/origins
endpoint (and in fact all EPC REST API endpoints) should not be accessed directly from code executing in the users browser, for two primary reasons:
This may imply compromising your super-sensitive EPC oAuth API
client_id
andclient_secret
- which are required to authenticate against the EPC API and invoke the/origins
endpointThe
/origins
endpoint may return sensitive information based on your applications entitlements, such as PII borrower information and user credentials for your integration, which you may not need to expose to the user-interface (a borrowers SSN, for example)
User-interface:
...
// Initializor function to update application state with origin data
const initializeOriginInformation = async () => {
// Access origin context
const proxy = await host;
const originContext = await proxy.getTransactionOrigin();
const originId = originContext.id;
const partnerAccessToken = originContext.partnerAccessToken;
// Initialize application state with origin information
const origin = OriginService(originId, partnerAccessToken);
const originLoanData = await origin.getOriginLoanData();
setInitializationInformation(originLoanData);
};
...
In the sample above - the
transaction.getOrigin
function is encapsulated within thehost
objectsgetTransactionOrigin
method. Thehost
object here is an instance of theHostConnectionService
module that encapsulates all interactions with the JavaScript API.You can see its implementation at
.../epc-hello-world/user-interface/src/services/HostConnectionService/HostConnectionService.js
Back-end:
The controller function bound to the back-ends GET /origins/:id
operation invokes the back-ends origin utility service to retrieve the information from the EPC REST API:
from services.origins import get_loan_information
"""" Origins retrieve controller """
def retrieve(origin_id, partner_access_token):
res = get_loan_information(origin_id, partner_access_token)
if res is False: # This means the credentials are not valid
res['status'] = "Invalid username or password"
return res, 401
else: # if credentials are valid simply pass on the response
return res, 200
...
"""" Internal function to pull down Origin information """
def _get_origin(origin_id, partner_access_token) -> dict:
# Ask token_manager for current oAuth token
access_token = app.token_manager.get_token()
url = EPC_BASE_URL + EPC_ORIGINS_URL + origin_id
headers = {
'Authorization': 'Bearer' + ' ' + access_token,
'X-Elli-PAT': partner_access_token
}
response = get(url=url, headers=headers)
return response
...
"""" Public function utilized by Origin controller """
def get_loan_information(origin_id, partner_access_token) -> dict:
response = dict()
origin_data = _get_origin(origin_id, partner_access_token)
if origin_data.status_code == 200:
origin_data_obj = origin_data.json()
if validate_credentials(origin_data_obj) is True:
if origin_data_obj.get("loan", None) is not None:
loan_information = origin_data_obj.get("loan", None)
return loan_information
elif origin_data_obj.get('errors', None) is not None:
response['errors'] = origin_data_obj.get('errors', None)
return response
else:
response['errors'] = "Loan Object not found"
return response
else:
return False
else:
response['errors'] = origin_data.status_code
return response
...
If all goes well and there are no exceptions raised in this workflow - the user-interface receives the origin information and is able to render the order form! 🎉
Case: Existing Transaction
If ZipRight determines it is being launched in the context of an existing transaction - it routes to its transaction details view. Upon rendering this view - it queries the back-end for the status of the transaction (along with the EPC resource
identifier for the mock PDF report it generates):
User-interface:
...
import StatusService from '../../services/StatusService/StatusService';
...
// Function that asynchronously initializes order information
const initializeOrderInformation = async (transactionId) => {
// Get order status information based on available transaction ID
const statusTracker = StatusService(transactionId);
const response = await statusTracker.getStatus();
// Initialize component state with order information
setOrderInformation(response);
};
...
...
// Internal function that invokes the integrations back-end API to access
// transaction processing status
const _getStatus = async () => {
// Prepare API URL
const url = API_URL + STATUS_PATH + transactionId;
try {
// Access and serialize the response
const response = await fetch(url);
const responseBody = await response.json();
return responseBody;
} catch (error) {
console.error(error);
};
};
...
Back-end:
The ZipRight back-end exposes a transaction status request endpoint, so that the ZipRight user-interface can fetch and display the processing status of a transaction that it maintains. For simplicities sake, the ZipRight back-end code currently does not implement any entitlement or authorization checks. The following code snippet shows how ZipRight processes a request for status exposed at its GET /v1/status/:id
endpoint:
...
""""" Controller for transaction status and details query """
def retrieve(id):
res = dict()
if app.request_tracker.get(id, None) != None:
req_tracker = app.request_tracker.get(id, 'None')
res['status'] = req_tracker.get('status', 'None')
res['resource_id'] = req_tracker.get('resource_id', 'None')
else:
return 404
return res, 200
If all goes well and there are no exceptions raised in this workflow - the user-interface receives the transaction status information and is able to render the order details view! 🎉
Updated over 4 years ago