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