cancel
Showing results for 
Search instead for 
Did you mean: 

Hosted checkout flow

I am trying to implement a hosted checkout ui. I used to generate the signature and other details from the backend and then used HTML for loading the page. But unfortunately, I am getting unauthorized and session time-out errors.
Can anyone help with this?
Here is the sample code

public ResponseEntity<Map<String,Object>> initiateHostedCheckout(@RequestParam Long userId,
@RequestParam(required=false) String amount) throws Exception {
String reference = "ref-" + java.util.UUID.randomUUID();

LinkedHashMap<String,String> base = SecureAcceptanceSigner.buildBaseFields(
props.getAccessKey(),
props.getProfileId(),
"authorization,create_payment_token", // transaction type to create payment token
reference,
amount == null ? "0.00" : amount,
"LKR",
String.valueOf(userId),
props.getReturnUrl(),
props.getSilentPostUrl()
);

String dataToSign = SecureAcceptanceSigner.buildDataToSign(base);
String signature = SecureAcceptanceSigner.computeHmacBase64(props.getSecretKey(), dataToSign);

// Build response map containing redirect URL and params
Map<String,String> params = new java.util.LinkedHashMap<>(base);
params.put("signature", signature);

Map<String,Object> resp = Map.of(
"redirectUrl", props.getHostedCheckoutUrl(),
"params", params
);
return ResponseEntity.ok(resp);
}
public class SecureAcceptanceSigner {

public static final DateTimeFormatter ISO = DateTimeFormatter.ISO_INSTANT;


public static LinkedHashMap<String,String> buildBaseFields(String accessKey, String profileId,
String transactionType, String referenceNumber,
String amount, String currency, String customerId,
String returnUrl, String silentPostUrl) {
LinkedHashMap<String,String> m = new LinkedHashMap<>();
String txnUuid = UUID.randomUUID().toString();
String signedDateTime = OffsetDateTime.now().format(ISO);
m.put("unsigned_field_names", "");
m.put("access_key", accessKey);
m.put("profile_id", profileId);
m.put("transaction_uuid", txnUuid);
m.put("signed_date_time", signedDateTime);
m.put("locale", "en");
m.put("transaction_type", transactionType);
m.put("reference_number", referenceNumber);
m.put("currency", currency == null ? "LKR" : currency);
m.put("amount", amount == null ? "0.00" : amount);
// signed_field_names - the order here determines the signing string order
String signedFieldNames = String.join(",",
"access_key",
"profile_id",
"transaction_uuid",
"signed_field_names",
"unsigned_field_names",
"signed_date_time",
"locale",
"transaction_type",
"reference_number",
"amount",
"currency"
// "customer_id",
// "payment_tokenization",
// "payment_method"
// "override_custom_cancel_page",
// "silent_post_url"
);
m.put("signed_field_names", signedFieldNames);
// m.put("payment_method", "card");

// m.put("customer_id", customerId == null ? "" : customerId);
// m.put("payment_tokenization", "true");

// m.put("override_custom_receipt_page", returnUrl == null ? "" : returnUrl);
// m.put("override_custom_cancel_page", returnUrl == null ? "" : returnUrl);
// m.put("silent_post_url", silentPostUrl == null ? "" : silentPostUrl);

return m;
}


public static String buildDataToSign(LinkedHashMap<String,String> orderedMap) {
String signedFieldsCsv = orderedMap.get("signed_field_names");
List<String> signedFieldNames = Arrays.stream(signedFieldsCsv.split(","))
.map(String::trim).toList();

List<String> parts = new ArrayList<>();
for (String field : signedFieldNames) {
String value;
if ("signed_field_names".equals(field)) {
value = signedFieldsCsv;
} else {
value = orderedMap.getOrDefault(field, "");
}
parts.add(field + "=" + value);
}
return String.join(",", parts);
}

public static String computeHmacBase64(String secretKey, String data) throws Exception {
Mac mac = Mac.getInstance("HmacSHA256");
SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
mac.init(keySpec);
byte[] raw = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(raw);
}
}
 
Screenshot 2025-12-01 160535.png

Screenshot 2025-12-01 124227.png



chamaran
Member
1 REPLY 1

Unauthorized and session timeout errors usually occur when required fields are missing or mismatched in the signature. Ensure all signed and unsigned fields expected by the hosted checkout API are included, the signature is computed correctly, and the timestamp is current. Also, make sure customer_id, returnUrl, and silentPostUrl are properly set and match your account configuration.

Elio
Contributor