Fetching the latest order with status (Java)
In this use case, the user refreshes the order screen to get the latest updates. On the other hand, the seller pushes updates to the application as and when there is a change to the fulfillment status of the order.
Code snippets
Client calls the BAP server to trigger status:
@PostMapping("/local_retail/order_status")
public ResponseEntity orderStatus(
@RequestHeader HttpHeaders headers,
@RequestBody ClientOrderRequest request) {
var response = bapApplicationService.orderStatus(request, headers);
return ResponseEntity.ok(response);
}
BAP server generates the protocol request body
/*
Example Request JSON:
{
"context": {
"domain": "nic2004:52110",
"country": "IND",
"city": "std:080",
"action": "status",
"core_version": "0.9.1",
"bap_id": "https://mock_bap.com/",
"bap_uri": "https://mock_bap.com/beckn/",
"bpp_id": "https://mock_bpp.com/",
"bpp_uri": "https://mock_bpp.com/beckn/",
"transaction_id": "1209849124",
"message_id": "123412423423",
"timestamp": "2021-03-23T10:00:40.065Z"
},
"message": {
"order_id": "order_1"
}
}
*/
public Response orderStatus(ClientOrderRequest request, HttpHeaders headers) {
// Generate message id
var messageId = UUID.randomUUID().toString();
// Check if transaction id exists in the request.
// Generate if not exists
var txnId = StringUtils.hasText(request.getTransactionId())
? request.getTransactionId()
: UUID.randomUUID().toString();
// Construct the Context based on the request parameters
var context = Context.builder().domain(request.getDomain())
.action(Context.ActionEnum.status)
.messageId(messageId)
.transactionId(txnId)
.transactionId(UUID.randomUUID().toString())
.timestamp(new Date().toString())
.build();
// Construct the protocol specific object to be passed to the BPP
// to get the status of the given order id
var statusRequest = StatusRequest.builder()
.context(context)
.message(StatusMessage.builder()
.orderId(request.getOrderId())
.build())
.build();
return invokeStatus(statusRequest, headers);
}
}
BAP server calls protocol status to the network
public Response invokeStatus(StatusRequest request, HttpHeaders headers) {
// Call to look up function which returns the the public key and BPP Endpoint to be called
var url = lookUp(headers);
// Call BPP Status api from the returned endpoint.
// Construct request headers with the public key
var responseEntity = apiClient.post(url[0] + Context.ActionEnum.status,
constructRequestHeaders(),
request,
Response.class);
// Validate the received response
if (responseEntity.getBody() == null || responseEntity.getBody().getError() != null ||
"NACK".equals(responseEntity.getBody().getMessage().getAck().getStatus())) {
// Return custom error to the client
return null;
}
return Response.of("ACK", null);
}
BAP receives protocol on_status
/*
Example Response JSON:
{
"context": {
"domain": "nic2004:52110",
"country": "IND",
"city": "std:080",
"action": "on_status",
"core_version": "0.9.1",
"bap_id": "https://mock_bap.com/",
"bap_uri": "https://mock_bap.com/beckn/",
"bpp_id": "https://mock_bpp.com/",
"bpp_uri": "https://mock_bpp.com/beckn/",
"transaction_id": "1209849124",
"message_id": "12341242343",
"timestamp": "2021-03-23T10:00:40.065Z"
},
"message": {
"order": {
"id": "order_1",
"state": "Picked from Store",
"items":[
{
"id": "item_1",
"quantity": {
"count": 2
}
},
{
"id": "item_2",
"quantity": {
"count": 1
}
},
],
"billing": {
"name": "John Doe",
"address": {
"door": "21A",
"name": "ABC Apartments",
"locality": "HSR Layout",
"city": "Bengaluru",
"state": "Karnataka",
"country": "India",
"area_code": "560102"
},
"email": "[email protected]",
"phone": "+919876543210"
},
"fulfillment": {
"type": "home-delivery",
"tracking": false,
"start": {
"location": {
"id": "koramangala-4th-block-location",
"descriptor": {
"name": "Pooja Stores"
},
"gps": "12.9349377,77.6055586"
},
"time": {
"range": {
"start": "2021-06-15T07:09:30.000Z",
"end": "2021-06-15T07:10:30.000Z"
}
},
"instructions": {
"name": "pick up instructions",
"short_desc": "Provide the order id"
},
"contact": {
"phone": "+919999999999",
"email": "[email protected]"
}
},
"end": {
"location": {
"gps": "12.914028, 77.638698",
"address": {
"door": "21A",
"name": "ABC Apartments",
"locality": "HSR Layout",
"city": "Bengaluru",
"state": "Karnataka",
"country": "India",
"area_code": "560102"
}
},
"time": {
"range": {
"start": "2021-06-15T07:11:36.212Z",
"end": "2021-06-15T07:12:36.212Z"
}
},
"instructions": {
"name": "drop off instructions",
"short_desc": "Leave at door step"
},
"contact": {
"phone": "+919876543210",
"email": "[email protected]"
}
}
},
"quote": {
"price": {
"currency": "INR",
"value": "370"
},
"breakup": [
{
"title": "Red Apples",
"price": {
"currency": "INR",
"value": "180"
}
},
{
"title": "Green Apples Organic",
"price": {
"currency": "INR",
"value": "170"
}
},
{
"title": "Delivery Charges",
"price": {
"currency": "INR",
"value": "20"
}
}
]
},
"payment": {
"uri": "https://api.bpp.com/pay?amt=$640&mode=upi&vpa=bpp@upi",
"tl_method": "http/get",
"params": {
"transaction_id": "transaction_453",
"amount": "640",
"mode": "upi",
"vpa": "bpp@upi"
},
"type": "ON-FULFILLMENT",
"status": "NOT-PAID"
}
}
}
}
*/
@PostMapping("/bap/on_status")
public ResponseEntity onStatus(
@RequestHeader HttpHeaders headers,
@RequestBody OnStatusRequest request) {
var response = bapCallbackApplicationService.onStatus(request, headers);
return ResponseEntity.ok(response);
}
public Response onStatus(OnStatusRequest request, HttpHeaders headers) {
// Validate the headers received
var isHeadersValid = validateHeaders(headers);
// Construct and return error if the received headers are invalid
if (!isHeadersValid) return null;
// Store the data received based on message id for the client to poll
saveToDB(request);
return Response.of("ACK", null);
}
Client polls BAP to get the on_status results
// Endpoint for the client to poll the order status based on the message id
@GetMapping("/local_retail/on_order_status")
public ResponseEntity onOrderStatus(
@PathVariable(ClientRoutes.PARAM_MESSAGE_ID) String messageId,
@RequestHeader HttpHeaders headers) {
var data = bapApplicationService.get(messageId);
return ResponseEntity.ok(data);
}