Derive PayU return URLs from property
Some checks failed
build-and-deploy / build-deploy (push) Failing after 30s

This commit is contained in:
androidlover5842
2026-01-30 05:53:06 +05:30
parent 4168835d47
commit 461e94edd0
7 changed files with 35 additions and 51 deletions

View File

@@ -28,40 +28,11 @@ class PayuSettingsSchemaFix(
salt_32 varchar, salt_32 varchar,
salt_256 varchar, salt_256 varchar,
base_url varchar not null, base_url varchar not null,
success_url varchar not null,
failure_url varchar not null,
use_salt_256 boolean not null default true, use_salt_256 boolean not null default true,
updated_at timestamptz not null updated_at timestamptz not null
) )
""".trimIndent() """.trimIndent()
) )
} }
val hasSuccessUrl = jdbcTemplate.queryForObject(
"""
select count(*)
from information_schema.columns
where table_name = 'payu_settings'
and column_name = 'success_url'
""".trimIndent(),
Int::class.java
) ?: 0
if (hasSuccessUrl == 0) {
logger.info("Adding payu_settings.success_url column")
jdbcTemplate.execute("alter table payu_settings add column success_url varchar")
}
val hasFailureUrl = jdbcTemplate.queryForObject(
"""
select count(*)
from information_schema.columns
where table_name = 'payu_settings'
and column_name = 'failure_url'
""".trimIndent(),
Int::class.java
) ?: 0
if (hasFailureUrl == 0) {
logger.info("Adding payu_settings.failure_url column")
jdbcTemplate.execute("alter table payu_settings add column failure_url varchar")
}
} }
} }

View File

@@ -117,8 +117,8 @@ class PayuQrPayments(
add("firstname", firstname) add("firstname", firstname)
add("email", email) add("email", email)
add("phone", phone) add("phone", phone)
add("surl", settings.successUrl) add("surl", buildReturnUrl(propertyId, true))
add("furl", settings.failureUrl) add("furl", buildReturnUrl(propertyId, false))
add("pg", "DBQR") add("pg", "DBQR")
add("bankcode", "UPIDBQR") add("bankcode", "UPIDBQR")
add("hash", hash) add("hash", hash)
@@ -191,6 +191,11 @@ class PayuQrPayments(
?: throw ResponseStatusException(HttpStatus.BAD_REQUEST, "PayU salt missing") ?: throw ResponseStatusException(HttpStatus.BAD_REQUEST, "PayU salt missing")
} }
private fun buildReturnUrl(propertyId: UUID, success: Boolean): String {
val path = if (success) "success" else "failure"
return "https://api.hoteltrisolaris.in/properties/$propertyId/payu/return/$path"
}
private fun sha512(input: String): String { private fun sha512(input: String): String {
val bytes = MessageDigest.getInstance("SHA-512").digest(input.toByteArray()) val bytes = MessageDigest.getInstance("SHA-512").digest(input.toByteArray())
return bytes.joinToString("") { "%02x".format(it) } return bytes.joinToString("") { "%02x".format(it) }

View File

@@ -0,0 +1,26 @@
package com.android.trisolarisserver.controller
import org.springframework.http.HttpStatus
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.ResponseStatus
import org.springframework.web.bind.annotation.RestController
import java.util.UUID
@RestController
@RequestMapping("/properties/{propertyId}/payu/return")
class PayuReturnController {
@GetMapping("/success")
@ResponseStatus(HttpStatus.NO_CONTENT)
fun success(@PathVariable propertyId: UUID) {
// PayU redirect target; no-op.
}
@GetMapping("/failure")
@ResponseStatus(HttpStatus.NO_CONTENT)
fun failure(@PathVariable propertyId: UUID) {
// PayU redirect target; no-op.
}
}

View File

@@ -53,10 +53,6 @@ class PayuSettingsController(
throw ResponseStatusException(HttpStatus.BAD_REQUEST, "merchantKey required") throw ResponseStatusException(HttpStatus.BAD_REQUEST, "merchantKey required")
} }
val baseUrl = request.baseUrl?.trim()?.ifBlank { null } ?: "https://secure.payu.in/_payment" val baseUrl = request.baseUrl?.trim()?.ifBlank { null } ?: "https://secure.payu.in/_payment"
val successUrl = request.successUrl?.trim()?.ifBlank { null }
?: throw ResponseStatusException(HttpStatus.BAD_REQUEST, "successUrl required")
val failureUrl = request.failureUrl?.trim()?.ifBlank { null }
?: throw ResponseStatusException(HttpStatus.BAD_REQUEST, "failureUrl required")
val existing = payuSettingsRepo.findByPropertyId(propertyId) val existing = payuSettingsRepo.findByPropertyId(propertyId)
val updated = if (existing == null) { val updated = if (existing == null) {
PayuSettings( PayuSettings(
@@ -65,8 +61,6 @@ class PayuSettingsController(
salt32 = request.salt32?.trim()?.ifBlank { null }, salt32 = request.salt32?.trim()?.ifBlank { null },
salt256 = request.salt256?.trim()?.ifBlank { null }, salt256 = request.salt256?.trim()?.ifBlank { null },
baseUrl = baseUrl, baseUrl = baseUrl,
successUrl = successUrl,
failureUrl = failureUrl,
useSalt256 = request.useSalt256 ?: true, useSalt256 = request.useSalt256 ?: true,
updatedAt = OffsetDateTime.now() updatedAt = OffsetDateTime.now()
) )
@@ -75,8 +69,6 @@ class PayuSettingsController(
if (request.salt32 != null) existing.salt32 = request.salt32.trim().ifBlank { null } if (request.salt32 != null) existing.salt32 = request.salt32.trim().ifBlank { null }
if (request.salt256 != null) existing.salt256 = request.salt256.trim().ifBlank { null } if (request.salt256 != null) existing.salt256 = request.salt256.trim().ifBlank { null }
existing.baseUrl = baseUrl existing.baseUrl = baseUrl
existing.successUrl = successUrl
existing.failureUrl = failureUrl
if (request.useSalt256 != null) existing.useSalt256 = request.useSalt256 if (request.useSalt256 != null) existing.useSalt256 = request.useSalt256
existing.updatedAt = OffsetDateTime.now() existing.updatedAt = OffsetDateTime.now()
existing existing
@@ -91,8 +83,6 @@ private fun PayuSettings.toResponse(): PayuSettingsResponse {
propertyId = propertyId, propertyId = propertyId,
merchantKey = merchantKey, merchantKey = merchantKey,
baseUrl = baseUrl, baseUrl = baseUrl,
successUrl = successUrl,
failureUrl = failureUrl,
useSalt256 = useSalt256, useSalt256 = useSalt256,
hasSalt32 = !salt32.isNullOrBlank(), hasSalt32 = !salt32.isNullOrBlank(),
hasSalt256 = !salt256.isNullOrBlank() hasSalt256 = !salt256.isNullOrBlank()

View File

@@ -7,8 +7,6 @@ data class PayuSettingsUpsertRequest(
val salt32: String? = null, val salt32: String? = null,
val salt256: String? = null, val salt256: String? = null,
val baseUrl: String? = null, val baseUrl: String? = null,
val successUrl: String? = null,
val failureUrl: String? = null,
val useSalt256: Boolean? = null val useSalt256: Boolean? = null
) )
@@ -16,8 +14,6 @@ data class PayuSettingsResponse(
val propertyId: UUID, val propertyId: UUID,
val merchantKey: String, val merchantKey: String,
val baseUrl: String, val baseUrl: String,
val successUrl: String,
val failureUrl: String,
val useSalt256: Boolean, val useSalt256: Boolean,
val hasSalt32: Boolean, val hasSalt32: Boolean,
val hasSalt256: Boolean val hasSalt256: Boolean

View File

@@ -40,12 +40,6 @@ class PayuSettings(
@Column(name = "base_url", nullable = false) @Column(name = "base_url", nullable = false)
var baseUrl: String = "https://secure.payu.in/_payment", var baseUrl: String = "https://secure.payu.in/_payment",
@Column(name = "success_url", nullable = false)
var successUrl: String,
@Column(name = "failure_url", nullable = false)
var failureUrl: String,
@Column(name = "use_salt_256", nullable = false) @Column(name = "use_salt_256", nullable = false)
var useSalt256: Boolean = true, var useSalt256: Boolean = true,

View File

@@ -11,6 +11,7 @@ internal object PublicEndpoints {
private val roomTypeImages = Regex("^/properties/[^/]+/room-types/[^/]+/images$") private val roomTypeImages = Regex("^/properties/[^/]+/room-types/[^/]+/images$")
private val iconPngFile = Regex("^/icons/png/[^/]+$") private val iconPngFile = Regex("^/icons/png/[^/]+$")
private val payuWebhook = Regex("^/properties/[^/]+/payu/webhook$") private val payuWebhook = Regex("^/properties/[^/]+/payu/webhook$")
private val payuReturn = Regex("^/properties/[^/]+/payu/return/(success|failure)$")
fun isPublic(request: HttpServletRequest): Boolean { fun isPublic(request: HttpServletRequest): Boolean {
val path = request.requestURI val path = request.requestURI
@@ -25,6 +26,7 @@ internal object PublicEndpoints {
|| (roomTypes.matches(path) && method == "GET") || (roomTypes.matches(path) && method == "GET")
|| roomTypeImages.matches(path) || roomTypeImages.matches(path)
|| (payuWebhook.matches(path) && method == "POST") || (payuWebhook.matches(path) && method == "POST")
|| (payuReturn.matches(path) && method == "GET")
|| (path == "/image-tags" && method == "GET") || (path == "/image-tags" && method == "GET")
|| path == "/icons/png" || path == "/icons/png"
|| iconPngFile.matches(path) || iconPngFile.matches(path)