Confirmation of a prepaid order (Java)
In this use case, the user confirms the delivery request by sending the proof of payment along with the other details present in the invoice and receives a confirmed order with the latest status of the fulfillment. In the case of hyperlocal delivery, the response sometimes also contains the details of the agent allocated to perform the delivery.
Code snippets
Client calls the BAP server to trigger confirm:
@PostMapping("/delivery/confirm_prepaird_order")
public ResponseEntity confirmPrepaidOrder(
@RequestHeader HttpHeaders headers,
@RequestBody ClientConfirmOrderRequest request) {
var response = bapApplicationService.confirmOrder(request, headers);
return ResponseEntity.ok(response);
}
BAP server generates the protocol request body
/*
Example Request JSON:
{
"context": {
"domain": "delivery",
"country": "IND",
"city": "std:080",
"action": "confirm",
"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": {
"provider": {
"id": "./logistics.hyperlocal-delivery/ind.blr/[email protected]"
},
"items": [
{
"id": "standard",
"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",
"created_at": "2021-06-15T07:08:36.211Z",
"updated_at": "2021-06-15T07:08:36.211Z"
},
"fulfillment": {
"type": "home-delivery",
"tracking": false,
"start": {
"location": {
"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": "Don't ring doorbell"
},
"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": "40"
},
"breakup": [
{
"title": "Standard delivery",
"price": {
"currency": "INR",
"value": "40"
}
}
],
"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": "40",
"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 confirmOrder(ClientConfirmOrderRequest 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.confirm)
.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 confirm the order for the selected items with payment details
var items = ClientAssembler.of(request.getItems());
var billing = ClientAssembler.of(request.getBilling());
var fulfilmentDetails = ClientAssembler.of(request.getFulfilment());
var quotation = ClientAssembler.of(request.getQuote());
var payment = ClientAssembler.of(request.getPayment());
var confirmOrderRequest = ConfirmRequest.builder()
.context(context)
.message(Message.builder().order(Order.builder()
.billing(billing)
.items(items)
.quote(quotation)
.payment(payment)
.build()).build())
.build();
return invokeConfirm(confirmOrderRequest, headers);
}
}
BAP server calls protocol confirm to the network
public Response invokeConfirm(ConfirmRequest 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 Confirm api from the returned endpoint.
// Construct request headers with the public key
var responseEntity = apiClient.post(url[0] + Context.ActionEnum.confirm,
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_confirm
/*
Example Response JSON:
{
"context": {
"domain": "delivery",
"country": "IND",
"city": "std:080",
"action": "on_confirm",
"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": "0f8c1e68-c041-427d-9ef4-d4d3e5b22ef9",
"state": "Active",
"items": [
{
"id": "standard",
"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",
"created_at": "2021-06-15T07:08:36.211Z",
"updated_at": "2021-06-15T07:08:36.211Z"
},
"fulfillment": {
"type": "home-delivery",
"tracking": false,
"start": {
"location": {
"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": "Don't ring doorbell"
},
"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": "40"
},
"breakup": [
{
"title": "Standard delivery",
"price": {
"currency": "INR",
"value": "40"
}
}
],
"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": "40",
"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_confirm")
public ResponseEntity onConfirm(
@RequestHeader HttpHeaders headers,
@RequestBody OnConfirmRequest request) {
var response = bapCallbackApplicationService.onConfirm(request, headers);
return ResponseEntity.ok(response);
}
public Response onConfirm(OnConfirmRequest 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_confirm results
// Endpoint for the client to poll the confirmed order based on the message id
@GetMapping("/delivery/on_confirm")
public ResponseEntity onConfirm(
@PathVariable(ClientRoutes.PARAM_MESSAGE_ID) String messageId,
@RequestHeader HttpHeaders headers) {
var data = bapApplicationService.get(messageId);
return ResponseEntity.ok(data);
}