Cancellation of an order with reason for cancellation (Java)

In this use case, the user cancels the order. Sometimes, the pharmacy asks for a reason for cancellation before cancelling the order that the user has to provide. The pharmacy’s cancellation policy is also communicated to the user on or before the cancellation of the order. The cancellation policy includes terms like cancellation charges, last time of cancellation, refund policy etc

Cancel

Code snippets

Client calls the BAP server to trigger cancel:

    @PostMapping("/healthcare_pharmacy/cancel_order")
    public ResponseEntity cancelOrder(
            @RequestHeader HttpHeaders headers,
            @RequestBody ClientOrderRequest request) {
        var response = bapApplicationService.cancelOrder(request, headers);
        return ResponseEntity.ok(response);
    }

BAP server generates the protocol request body


/*
Example Request JSON:
{
    "context": {
        "domain": "nic2004:52311",
        "country": "IND",
        "city": "std:080",
        "action": "cancel",
        "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-03-23T10:00:40.065Z"
    },
    "message": {
        "order_id": "order_1",
        "cancellation_reason_id": "reason_3"
    }
}
*/

    public Response cancelOrder(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.cancel)
                .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 cancel the order of given order id
        // The valid cancellation reasons will be fetched by the BPP's /get_cancellation_reasons endpoint
        var cancelRequest = CancelRequest.builder()
                .context(context)
                .message(CancelMessage.builder()
                        .orderId(request.getOrderId())
                        .cancellationReasonId(request.getCancellationReason())
                        .build())
                .build();

        return invokeCancel(cancelRequest, headers);
    }
}

BAP server calls protocol cancel to the network

    public Response invokeCancel(CancelRequest 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 Cancel api from the returned endpoint.
        // Construct request headers with the public key
        var responseEntity = apiClient.post(url[0] + Context.ActionEnum.cancel,
                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_cancel

/*
Example Response JSON:
{
    "context": {
        "domain": "nic2004:52311",
        "country": "IND",
        "city": "std:080",
        "action": "on_status",
        "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-03-23T10:00:40.065Z"
    },
    "message": {
        "order": {
            "id": "order_1",
            "state": "Cancelled",
            "items": [
                {
                    "id": "vit_c",
                    "price" : {
                        "currency": "INR",
                        "value": "500"
                    },
                    "quantity": {
                        "selected": {
                            "count": 1
                        }
                    }
                },
                {
                    "id": "a_500",
                    "price" : {
                        "currency": "INR",
                        "value": "500"
                    },
                    "quantity": {
                        "selected": {
                            "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",
                "start": {
                    "location": {
                        "id": "abc_medicines_koramangala",
                        "descriptor": {
                            "name": "ABC Medicines"
                        },
                        "gps": "12.9349377,77.6055586"
                    },
                    "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]"
                    }
                },
                "customer": {
                    "person":{
                        "name": "John Doe",
                        "image": "https://mock_bap.com/prescription1.jpg",
                        "dob": "1991-08-12"
                    }
                }
            },
            "quote": {
                "price": {
                    "currency": "INR",
                    "value": "1100"
                },
                "breakup": [
                    {
                        "title": "Vitamin C",
                        "price": {
                            "currency": "INR",
                            "value": "500"
                        }
                    }, 
                   {
                        "title": "Antibiotics",
                        "price": {
                            "currency": "INR",
                            "value": "500"
                        }
                    },
                    {
                        "title": "Delivery Charge",
                        "price": {
                            "currency": "INR",
                            "value": "100"
                        }
                    }
                ],
                "ttl": "P1H"
            },
            "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": "1100",
                    "mode": "upi",
                    "vpa": "bpp@upi"
                },
                "type": "ON-FULFILLMENT",
                "status": "NOT-PAID"
            }
        }
    }
}
*/
    @PostMapping("/bap/on_cancel")
    public ResponseEntity onCancel(
            @RequestHeader HttpHeaders headers,
            @RequestBody OnCancelRequest request) {
        var response = bapCallbackApplicationService.onCancel(request, headers);
        return ResponseEntity.ok(response);
    }

    public Response onCancel(OnCancelRequest 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_cancel results

    // Endpoint for the client to poll the cancelled order based on the message id
    @GetMapping("/healthcare_pharmacy/on_cancel_order")
    public ResponseEntity onCancelOrder(
            @PathVariable(ClientRoutes.PARAM_MESSAGE_ID) String messageId,
            @RequestHeader HttpHeaders headers) {
        var data = bapApplicationService.get(messageId);
        return ResponseEntity.ok(data);
    }