Updating billing details of an active order (Java)
In this use case, the user sometimes updates the billing details of the order after the order has been placed. The seller then updates the order with the new billing details and sends back the updated order object.
Code snippets
Client calls the BAP server to trigger update:
@PostMapping("/local_retail/update_billing_details")
public ResponseEntity updateBillingDetails(
@RequestHeader HttpHeaders headers,
@RequestBody ClientUpdateOrderRequest request) {
var response = bapApplicationService.updateOrder(request, headers);
return ResponseEntity.ok(response);
}
BAP server generates the protocol request body
/*
Example Request JSON:
{
"context": {
"domain": "local_retail",
"country": "IND",
"city": "std:080",
"action": "update",
"core_version": "0.9.2",
"bap_id": "https://mock_bap.com/",
"bap_uri": "https://mock_bap.com/beckn/",
"transaction_id": "1239890342",
"message_id": "123793824",
"timestamp": "2021-06-23T07:33:02.655Z"
},
"message": {
"update_target": "billing",
"order": {
"id":"298310283",
"state":"Order placed",
"provider": {
"id": "pooja-stores",
"locations": [
{
"id": "pooja-stores.koramangala"
}
]
},
"items": [
{
"id": "item_1",
"quantity": {
"count": 1
}
},
{
"id": "item_4",
"quantity": {
"count": 2
}
}
],
"billing": {
"name": "Jane Doe",
"address": {
"door": "21B",
"name": "EFG Apartments",
"locality": "Electronic City",
"city": "Bengaluru",
"state": "Karnataka",
"country": "India",
"area_code": "560104"
},
"email": "[email protected]",
"phone": "+919777543210",
"created_at": "2021-06-15T07:08:36.211Z",
"updated_at": "2021-06-15T07:08:36.211Z"
},
"fulfillment": {
"type": "home-delivery",
"tracking": false,
"start": {
"location": {
"id": "pooja-stores.koramangala",
"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.835407, 77.662524",
"address": {
"door": "21B",
"name": "EFG Apartments",
"locality": "Electronic City",
"city": "Bengaluru",
"state": "Karnataka",
"country": "India",
"area_code": "560104"
}
},
"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": "+919777543210",
"email": "[email protected]"
}
}
},
"quote": {
"price": {
"currency": "INR",
"value": "180"
},
"breakup": [
{
"title": "Brown Bread 400 gm",
"price": {
"currency": "INR",
"value": "40"
}
},
{
"title": "Good Life Toned Milk 1L",
"price": {
"currency": "INR",
"value": "120"
}
}
],
"ttl": "P4D"
},
"payment": {
"uri": "https://api.bpp.com/pay?amt=$amount&txn_id=ksh87yriuro34iyr3p4&mode=upi&vpa=bpp@upi",
"tl_method": "http/get",
"params": {
"transaction_id": "ksh87yriuro34iyr3p4",
"amount": "180",
"mode": "upi",
"vpa": "bpp@upi"
},
"type": "ON-ORDER",
"status": "PAID"
},
"created_at": "2021-06-23T07:33:02.655Z",
"updated_at": "2021-06-23T07:33:02.655Z"
}
}
}
*/
public Response updateOrder(ClientUpdateOrderRequest 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.update)
.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 update the order with the new billing details
var billing = ClientAssembler.of(request.getBilling());
var updateOrderRequest = UpdateRequest.builder()
.context(context)
.message(UpdateMessage.builder().order(Order.builder()
.id(request.getOrderId())
.billing(billing)
.build()).build())
.build();
return invokeUpdate(updateOrderRequest, headers);
}
}
BAP server calls protocol update to the network
public Response invokeUpdate(UpdateRequest 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 Update api from the returned endpoint.
// Construct request headers with the public key
var responseEntity = apiClient.post(url[0] + Context.ActionEnum.update,
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_update
/*
Example Response JSON:
{
"context": {
"domain": "local_retail",
"country": "IND",
"city": "std:080",
"action": "on_update",
"core_version": "0.9.2",
"bap_id": "https://mock_bap.com/",
"bap_uri": "https://mock_bap.com/beckn/",
"transaction_id": "1239890342",
"message_id": "123793824",
"timestamp": "2021-06-23T07:33:02.655Z"
},
"message": {
"order": {
"id":"298310283",
"state":"Order placed",
"provider": {
"id": "pooja-stores",
"locations": [
{
"id": "pooja-stores.koramangala"
}
]
},
"items": [
{
"id": "item_1",
"quantity": {
"count": 1
}
},
{
"id": "item_4",
"quantity": {
"count": 2
}
}
],
"billing": {
"name": "Jane Doe",
"address": {
"door": "21B",
"name": "EFG Apartments",
"locality": "Electronic City",
"city": "Bengaluru",
"state": "Karnataka",
"country": "India",
"area_code": "560104"
},
"email": "[email protected]",
"phone": "+919777543210",
"created_at": "2021-06-15T07:08:36.211Z",
"updated_at": "2021-06-15T07:08:36.211Z"
},
"fulfillment": {
"type": "home-delivery",
"tracking": false,
"start": {
"location": {
"id": "pooja-stores.koramangala",
"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.835407, 77.662524",
"address": {
"door": "21B",
"name": "EFG Apartments",
"locality": "Electronic City",
"city": "Bengaluru",
"state": "Karnataka",
"country": "India",
"area_code": "560104"
}
},
"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": "+919777543210",
"email": "[email protected]"
}
}
},
"quote": {
"price": {
"currency": "INR",
"value": "180"
},
"breakup": [
{
"title": "Brown Bread 400 gm",
"price": {
"currency": "INR",
"value": "40"
}
},
{
"title": "Good Life Toned Milk 1L",
"price": {
"currency": "INR",
"value": "120"
}
}
],
"ttl": "P4D"
},
"payment": {
"uri": "https://api.bpp.com/pay?amt=$amount&txn_id=ksh87yriuro34iyr3p4&mode=upi&vpa=bpp@upi",
"tl_method": "http/get",
"params": {
"transaction_id": "ksh87yriuro34iyr3p4",
"amount": "180",
"mode": "upi",
"vpa": "bpp@upi"
},
"type": "ON-ORDER",
"status": "PAID"
},
"created_at": "2021-06-23T07:33:02.655Z",
"updated_at": "2021-06-23T07:33:02.655Z"
}
}
}
*/
@PostMapping("/bap/on_update")
public ResponseEntity onUpdate(
@RequestHeader HttpHeaders headers,
@RequestBody OnUpdateRequest request) {
var response = bapCallbackApplicationService.onUpdate(request, headers);
return ResponseEntity.ok(response);
}
public Response onUpdate(OnUpdateRequest 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_update results
// Endpoint for the client to poll the order updateing based on the message id
@GetMapping("/local_retail/on_update_order")
public ResponseEntity onUpdateOrder(
@PathVariable(ClientRoutes.PARAM_MESSAGE_ID) String messageId,
@RequestHeader HttpHeaders headers) {
var data = bapApplicationService.get(messageId);
return ResponseEntity.ok(data);
}