Add PayU webhook capture per property
Some checks failed
build-and-deploy / build-deploy (push) Failing after 29s
Some checks failed
build-and-deploy / build-deploy (push) Failing after 29s
This commit is contained in:
@@ -0,0 +1,36 @@
|
|||||||
|
package com.android.trisolarisserver.config
|
||||||
|
|
||||||
|
import org.springframework.jdbc.core.JdbcTemplate
|
||||||
|
import org.springframework.stereotype.Component
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class PayuWebhookLogSchemaFix(
|
||||||
|
private val jdbcTemplate: JdbcTemplate
|
||||||
|
) : PostgresSchemaFix(jdbcTemplate) {
|
||||||
|
|
||||||
|
override fun runPostgres(jdbcTemplate: JdbcTemplate) {
|
||||||
|
val hasTable = jdbcTemplate.queryForObject(
|
||||||
|
"""
|
||||||
|
select count(*)
|
||||||
|
from information_schema.tables
|
||||||
|
where table_name = 'payu_webhook_log'
|
||||||
|
""".trimIndent(),
|
||||||
|
Int::class.java
|
||||||
|
) ?: 0
|
||||||
|
if (hasTable == 0) {
|
||||||
|
logger.info("Creating payu_webhook_log table")
|
||||||
|
jdbcTemplate.execute(
|
||||||
|
"""
|
||||||
|
create table payu_webhook_log (
|
||||||
|
id uuid primary key,
|
||||||
|
property_id uuid not null references property(id) on delete cascade,
|
||||||
|
headers text,
|
||||||
|
payload text,
|
||||||
|
content_type varchar,
|
||||||
|
received_at timestamptz not null
|
||||||
|
)
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package com.android.trisolarisserver.controller
|
||||||
|
|
||||||
|
import com.android.trisolarisserver.models.payment.PayuWebhookLog
|
||||||
|
import com.android.trisolarisserver.repo.PayuWebhookLogRepo
|
||||||
|
import com.android.trisolarisserver.repo.PropertyRepo
|
||||||
|
import jakarta.servlet.http.HttpServletRequest
|
||||||
|
import org.springframework.http.HttpStatus
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping
|
||||||
|
import org.springframework.web.bind.annotation.ResponseStatus
|
||||||
|
import org.springframework.web.bind.annotation.RestController
|
||||||
|
import org.springframework.web.server.ResponseStatusException
|
||||||
|
import java.time.OffsetDateTime
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/properties/{propertyId}/payu/webhook")
|
||||||
|
class PayuWebhookCapture(
|
||||||
|
private val propertyRepo: PropertyRepo,
|
||||||
|
private val payuWebhookLogRepo: PayuWebhookLogRepo
|
||||||
|
) {
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||||
|
fun capture(
|
||||||
|
@PathVariable propertyId: UUID,
|
||||||
|
@RequestBody(required = false) body: String?,
|
||||||
|
request: HttpServletRequest
|
||||||
|
) {
|
||||||
|
val property = propertyRepo.findById(propertyId).orElseThrow {
|
||||||
|
ResponseStatusException(HttpStatus.NOT_FOUND, "Property not found")
|
||||||
|
}
|
||||||
|
val headers = request.headerNames.toList().associateWith { request.getHeader(it) }
|
||||||
|
val headersText = headers.entries.joinToString("\n") { (k, v) -> "$k: $v" }
|
||||||
|
payuWebhookLogRepo.save(
|
||||||
|
PayuWebhookLog(
|
||||||
|
property = property,
|
||||||
|
headers = headersText,
|
||||||
|
payload = body,
|
||||||
|
contentType = request.contentType,
|
||||||
|
receivedAt = OffsetDateTime.now()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package com.android.trisolarisserver.models.payment
|
||||||
|
|
||||||
|
import com.android.trisolarisserver.models.property.Property
|
||||||
|
import jakarta.persistence.Column
|
||||||
|
import jakarta.persistence.Entity
|
||||||
|
import jakarta.persistence.FetchType
|
||||||
|
import jakarta.persistence.GeneratedValue
|
||||||
|
import jakarta.persistence.Id
|
||||||
|
import jakarta.persistence.JoinColumn
|
||||||
|
import jakarta.persistence.ManyToOne
|
||||||
|
import jakarta.persistence.Table
|
||||||
|
import java.time.OffsetDateTime
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "payu_webhook_log")
|
||||||
|
class PayuWebhookLog(
|
||||||
|
@Id
|
||||||
|
@GeneratedValue
|
||||||
|
@Column(columnDefinition = "uuid")
|
||||||
|
val id: UUID? = null,
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY, optional = false)
|
||||||
|
@JoinColumn(name = "property_id", nullable = false)
|
||||||
|
var property: Property,
|
||||||
|
|
||||||
|
@Column(name = "headers", columnDefinition = "text")
|
||||||
|
var headers: String? = null,
|
||||||
|
|
||||||
|
@Column(name = "payload", columnDefinition = "text")
|
||||||
|
var payload: String? = null,
|
||||||
|
|
||||||
|
@Column(name = "content_type")
|
||||||
|
var contentType: String? = null,
|
||||||
|
|
||||||
|
@Column(name = "received_at", nullable = false, columnDefinition = "timestamptz")
|
||||||
|
val receivedAt: OffsetDateTime = OffsetDateTime.now()
|
||||||
|
)
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package com.android.trisolarisserver.repo
|
||||||
|
|
||||||
|
import com.android.trisolarisserver.models.payment.PayuWebhookLog
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
interface PayuWebhookLogRepo : JpaRepository<PayuWebhookLog, UUID>
|
||||||
@@ -10,6 +10,7 @@ internal object PublicEndpoints {
|
|||||||
private val roomTypes = Regex("^/properties/[^/]+/room-types$")
|
private val roomTypes = Regex("^/properties/[^/]+/room-types$")
|
||||||
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$")
|
||||||
|
|
||||||
fun isPublic(request: HttpServletRequest): Boolean {
|
fun isPublic(request: HttpServletRequest): Boolean {
|
||||||
val path = request.requestURI
|
val path = request.requestURI
|
||||||
@@ -23,6 +24,7 @@ internal object PublicEndpoints {
|
|||||||
|| (roomsByType.matches(path) && method == "GET")
|
|| (roomsByType.matches(path) && method == "GET")
|
||||||
|| (roomTypes.matches(path) && method == "GET")
|
|| (roomTypes.matches(path) && method == "GET")
|
||||||
|| roomTypeImages.matches(path)
|
|| roomTypeImages.matches(path)
|
||||||
|
|| (payuWebhook.matches(path) && method == "POST")
|
||||||
|| (path == "/image-tags" && method == "GET")
|
|| (path == "/image-tags" && method == "GET")
|
||||||
|| path == "/icons/png"
|
|| path == "/icons/png"
|
||||||
|| iconPngFile.matches(path)
|
|| iconPngFile.matches(path)
|
||||||
|
|||||||
Reference in New Issue
Block a user