Updating an item’s quantity on an active order and receiving updated order with payment terms (Java)

In this use case, the user changes the quantity of one or more items after the order has been confirmed. If the user has already made the payment, the seller sends additional payment terms that the user needs to fulfill before the quantity gets updated. In case the order is postpaid, the seller can update the total amount to be paid after the fulfillment of the order.

Update

Code snippets

Client calls the BAP server to trigger update:

    @PostMapping("/local_retail/update_quantity")
    public ResponseEntity updateItemQuantity(
            @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": "items",
        "order": {
            "id":"298310283",
            "state":"Order placed",
            "provider": {
                "id": "pooja-stores",
                "locations": [
                    {
                        "id": "pooja-stores.koramangala"
                    }
                ]
            },
            "items": [
                {
                    "id": "item_1",
                    "quantity": {
                        "count": 2
                    }
                },
                {
                    "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": "200"
                },
                "breakup": [
                    {
                        "title": "Brown Bread 400 gm",
                        "price": {
                            "currency": "INR",
                            "value": "80"
                        }
                    },
                    {
                        "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": "160",
                    "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 given item quantity
        var item = ClientAssembler.of(request.getItem());
        var updateOrderRequest = UpdateRequest.builder()
                .context(context)
                .message(UpdateMessage.builder().order(Order.builder()
                        .id(request.getOrderId())
                        .item(item)
                        .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": "40",
                    "mode": "upi",
                    "vpa": "bpp@upi"
                },
                "type": "ON-ORDER",
                "status": "NOT-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);
    }