Summary|
Tags|
Feature:
api/rejected-payment-flow/rejected-payment-flow.feature|
Ticketing MVP - Rejected Payment Flow
As a Ticketing MVP automation
I want to execute the rejected payment path
So that I can verify the system correctly rejects declined payments without confirming a purchase
Scenario: [1:15]
HU-04 - Rejected Payment Flow (DECLINED)
ms: 101
>>
Background:
7
* def baseUrlEvents = karate.properties['baseUrlEvents'] || 'http://localhost:8081'
0
22:53:18.638 karate.env system property was: null
8
* def baseUrlTicketing = karate.properties['baseUrlTicketing'] || 'http://localhost:8082'
0
9
* def UUID = Java.type('java.util.UUID')
0
10
* def buyerId = UUID.randomUUID() + ''
0
11
* def buyerEmail = 'buyer-' + java.lang.System.currentTimeMillis() + '@karate-test.com'
0
12
* def eventTitle = 'Karate Rejected Payment Event ' + java.lang.System.currentTimeMillis()
0
13
* def futureDate = '2026-12-15T20:00:00'
0
# Setup API: Crear sala
18
Given url baseUrlEvents + '/api/v1/rooms'
0
19
And header X-Role = 'ADMIN'
0
20
And header X-User-Id = '00000000-0000-0000-0000-000000000001'
0
21
And request
0
{
"name": "Karate Rejected Payment Room",
"maxCapacity": 100
}
28
When method post
10
22:53:18.641 request:
1 > POST http://localhost:8081/api/v1/rooms
1 > X-Role: ADMIN
1 > X-User-Id: 00000000-0000-0000-0000-000000000001
1 > Content-Type: application/json; charset=UTF-8
1 > Content-Length: 57
1 > Host: localhost:8081
1 > Connection: Keep-Alive
1 > User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.17)
1 > Accept-Encoding: gzip,deflate
{"name":"Karate Rejected Payment Room","maxCapacity":100}
22:53:18.651 response time in milliseconds: 10
1 < 201
1 < Content-Type: application/json
1 < Transfer-Encoding: chunked
1 < Date: Wed, 08 Apr 2026 03:53:18 GMT
1 < Keep-Alive: timeout=60
1 < Connection: keep-alive
{"id":"08dc7179-ffe7-4321-8615-0b4d2f96d908","name":"Karate Rejected Payment Room","maxCapacity":100,"created_at":"2026-04-08T03:53:18.64456498","updated_at":"2026-04-08T03:53:18.644581791"}
29
Then status 201
0
30
* def roomId = response.id ? response.id : response.roomId
0
31
* match roomId == '#uuid'
0
# Setup API: Crear evento
# Nota técnica: el evento se crea inicialmente con estado interno DRAFT y luego se publica.
35
Given url baseUrlEvents + '/api/v1/events'
0
36
And header X-Role = 'ADMIN'
0
37
And header X-User-Id = '00000000-0000-0000-0000-000000000001'
0
38
And request
0
{
"roomId": "#(roomId)",
"title": "#(eventTitle)",
"description": "Test event for rejected payment flow",
"date": "#(futureDate)",
"capacity": 50,
"enableSeats": false
}
49
When method post
12
22:53:18.652 request:
2 > POST http://localhost:8081/api/v1/events
2 > X-Role: ADMIN
2 > X-User-Id: 00000000-0000-0000-0000-000000000001
2 > Content-Type: application/json; charset=UTF-8
2 > Content-Length: 219
2 > Host: localhost:8081
2 > Connection: Keep-Alive
2 > User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.17)
2 > Accept-Encoding: gzip,deflate
{"roomId":"08dc7179-ffe7-4321-8615-0b4d2f96d908","title":"Karate Rejected Payment Event 1775620398640","description":"Test event for rejected payment flow","date":"2026-12-15T20:00:00","capacity":50,"enableSeats":false}
22:53:18.664 response time in milliseconds: 12
2 < 201
2 < Content-Type: application/json
2 < Transfer-Encoding: chunked
2 < Date: Wed, 08 Apr 2026 03:53:18 GMT
2 < Keep-Alive: timeout=60
2 < Connection: keep-alive
{"id":"d9ddd7b8-3fb5-414c-8e67-647995fd11b4","roomId":"08dc7179-ffe7-4321-8615-0b4d2f96d908","title":"Karate Rejected Payment Event 1775620398640","description":"Test event for rejected payment flow","date":"2026-12-15T20:00:00","capacity":50,"status":"DRAFT","createdAt":"2026-04-08T03:53:18.657390916","updatedAt":"2026-04-08T03:53:18.657397444","createdBy":"00000000-0000-0000-0000-000000000001","imageUrl":null,"subtitle":null,"location":null,"director":null,"castMembers":null,"duration":null,"tag":null,"isLimited":false,"isFeatured":false,"enableSeats":false,"author":null}
50
Then status 201
0
51
* def eventId = response.id ? response.id : response.eventId
0
52
* match eventId == '#uuid'
0
53
* match response.status == 'DRAFT'
0
# HU-02: Configuración de tiers y precios por evento
56
Given url baseUrlEvents + '/api/v1/events/' + eventId + '/tiers'
0
57
And header X-Role = 'ADMIN'
0
58
And request
0
[
{
"tierType": "GENERAL",
"price": 100,
"quota": 40
}
]
68
When method post
12
22:53:18.666 request:
3 > POST http://localhost:8081/api/v1/events/d9ddd7b8-3fb5-414c-8e67-647995fd11b4/tiers
3 > X-Role: ADMIN
3 > Content-Type: application/json; charset=UTF-8
3 > Content-Length: 47
3 > Host: localhost:8081
3 > Connection: Keep-Alive
3 > User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.17)
3 > Accept-Encoding: gzip,deflate
[{"tierType":"GENERAL","price":100,"quota":40}]
22:53:18.677 response time in milliseconds: 11
3 < 201
3 < Content-Type: application/json
3 < Transfer-Encoding: chunked
3 < Date: Wed, 08 Apr 2026 03:53:18 GMT
3 < Keep-Alive: timeout=60
3 < Connection: keep-alive
{"eventId":"d9ddd7b8-3fb5-414c-8e67-647995fd11b4","tiers":[{"id":"69a020ca-e344-4018-9330-99facd37e71f","tierType":"GENERAL","price":100,"quota":40,"validFrom":null,"validUntil":null,"createdAt":"2026-04-08T03:53:18.671314115","updatedAt":"2026-04-08T03:53:18.671326674"}]}
69
Then status 201
0
70
* def tierBlock = response.tiers ? response.tiers[0] : response[0]
0
71
* def tierId = tierBlock.id ? tierBlock.id : tierBlock.tierId
0
72
* match tierId == '#uuid'
0
73
* match tierBlock.tierType == 'GENERAL'
0
# Setup API: Publicar evento
76
Given url baseUrlEvents + '/api/v1/events/' + eventId + '/publish'
0
77
And header X-Role = 'ADMIN'
0
78
When method patch
11
22:53:18.679 request:
4 > PATCH http://localhost:8081/api/v1/events/d9ddd7b8-3fb5-414c-8e67-647995fd11b4/publish
4 > X-Role: ADMIN
4 > Host: localhost:8081
4 > Connection: Keep-Alive
4 > User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.17)
4 > Accept-Encoding: gzip,deflate
22:53:18.689 response time in milliseconds: 10
4 < 200
4 < Content-Type: application/json
4 < Transfer-Encoding: chunked
4 < Date: Wed, 08 Apr 2026 03:53:18 GMT
4 < Keep-Alive: timeout=60
4 < Connection: keep-alive
{"id":"d9ddd7b8-3fb5-414c-8e67-647995fd11b4","roomId":"08dc7179-ffe7-4321-8615-0b4d2f96d908","title":"Karate Rejected Payment Event 1775620398640","description":"Test event for rejected payment flow","date":"2026-12-15T20:00:00","capacity":50,"status":"PUBLISHED","createdAt":"2026-04-08T03:53:18.657391","updatedAt":"2026-04-08T03:53:18.682794821","createdBy":"00000000-0000-0000-0000-000000000001","imageUrl":null,"subtitle":null,"location":null,"director":null,"castMembers":null,"duration":null,"tag":null,"isLimited":false,"isFeatured":false,"enableSeats":false,"author":null}
79
Then status 200
0
80
* match response.status == 'PUBLISHED'
0
# HU-04: Reserva y compra de entrada con pago simulado (Creación de reserva)
83
Given url baseUrlTicketing + '/api/v1/reservations'
0
84
And header X-User-Id = buyerId
0
85
And request
0
{
"eventId": "#(eventId)",
"tierId": "#(tierId)",
"buyerEmail": "#(buyerEmail)"
}
93
When method post
30
22:53:18.690 request:
5 > POST http://localhost:8082/api/v1/reservations
5 > X-User-Id: a675c8aa-330b-4828-8abd-a884541fab0a
5 > Content-Type: application/json; charset=UTF-8
5 > Content-Length: 149
5 > Host: localhost:8082
5 > Connection: Keep-Alive
5 > User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.17)
5 > Accept-Encoding: gzip,deflate
{"eventId":"d9ddd7b8-3fb5-414c-8e67-647995fd11b4","tierId":"69a020ca-e344-4018-9330-99facd37e71f","buyerEmail":"buyer-1775620398640@karate-test.com"}
22:53:18.720 response time in milliseconds: 29
5 < 201
5 < Content-Type: application/json
5 < Transfer-Encoding: chunked
5 < Date: Wed, 08 Apr 2026 03:53:18 GMT
5 < Keep-Alive: timeout=60
5 < Connection: keep-alive
{"id":"79312d3b-0347-478e-92b9-b223134a5e47","eventId":"d9ddd7b8-3fb5-414c-8e67-647995fd11b4","tierId":"69a020ca-e344-4018-9330-99facd37e71f","buyerId":"a675c8aa-330b-4828-8abd-a884541fab0a","status":"PENDING","createdAt":"2026-04-08T03:53:18.712951847","updatedAt":"2026-04-08T03:53:18.712951847","validUntilAt":"2026-04-08T04:03:18.712951847"}
94
Then status 201
0
95
* def reservationId = response.id
0
96
* match response == { id: '#uuid', eventId: '#uuid', tierId: '#uuid', buyerId: '#uuid', status: 'PENDING', createdAt: '#string', updatedAt: '#string', validUntilAt: '#string' }
0
# HU-04: Reserva y compra de entrada con pago simulado (Pago rechazado)
# Según GlobalExceptionHandler: PaymentFailedException → HTTP 400, body: { error, reservationId, status: "PAYMENT_FAILED", timestamp }
100
Given url baseUrlTicketing + '/api/v1/reservations/' + reservationId + '/payments'
0
101
And header X-User-Id = buyerId
0
102
And request
0
{
"amount": 100,
"paymentMethod": "MOCK",
"status": "DECLINED"
}
110
When method post
19
22:53:18.721 request:
6 > POST http://localhost:8082/api/v1/reservations/79312d3b-0347-478e-92b9-b223134a5e47/payments
6 > X-User-Id: a675c8aa-330b-4828-8abd-a884541fab0a
6 > Content-Type: application/json; charset=UTF-8
6 > Content-Length: 57
6 > Host: localhost:8082
6 > Connection: Keep-Alive
6 > User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.17)
6 > Accept-Encoding: gzip,deflate
{"amount":100,"paymentMethod":"MOCK","status":"DECLINED"}
22:53:18.740 response time in milliseconds: 19
6 < 400
6 < Content-Type: application/json
6 < Transfer-Encoding: chunked
6 < Date: Wed, 08 Apr 2026 03:53:18 GMT
6 < Connection: close
{"reservationId":"79312d3b-0347-478e-92b9-b223134a5e47","error":"Payment declined. Your reservation remains active for 10 minutes","status":"PAYMENT_FAILED","timestamp":"2026-04-08T03:53:18.738969813"}
111
Then status 400
0
112
* match response.status == 'PAYMENT_FAILED'
0
113
* match response.reservationId == reservationId
0
114
* match response == { error: '#string', reservationId: '#uuid', status: 'PAYMENT_FAILED', timestamp: '#string' }
0
115
* print 'Payment correctly rejected. Reservation status:', response.status
0
22:53:18.741 [print] Payment correctly rejected. Reservation status: PAYMENT_FAILED