thisago's blog
==============
------------------------------------------------------------------------
__________________________________________________
HOW I CANCELLED SEAT SELECTION IN GOL AIRLINES
DAMN WEBSITE
__________________________________________________
<2026-03-29 Sun>
Discuss in HN:
------------------------------------------------------------------------
I got stuck analyzing the Gol brazilian airlines check-in page[1] during
the whole last hour because I selected a seat. And only after selection
I realized: there's no UI button to remove the selection.
Even selecting a economic seat in the 48h period before the flight has a
cost[2], and yes, this kind of seat is what I bought previously.
Anyway, if I extend my personal complaining about websites of airline
companies, this content would be enough to fill a book LOL
I could just give up of this online check-in and do it from the airport,
but the NoScript temporary trust was already conceded, I had to make it
until the end.
[seems wrong as far as I understood about]
Analyzing
=========
The first step was analyze the network requests the website does. The
seat selection was *extremely* slow in my qube without GPU
acceleration, but this friction didn't feared me to keep devtools
open.
With the intention to aggregate useful identifiers about my
order/user, I copied the curl representation of some of the main
requests:
- [Check-in data retrieval]
- Personal data validation. A POST that confirms phone, CPF
(government ID) and email (`POST
https://g2s-checkin-api.voegol.com.br/api/passenger/update')
- [Seat selection]
Then, I tried to infer the design of the API to bet the deletion
method/parameters.
It didn't worked, so what I did was to analyze the JavaScript minified
bundle files of the webpage. With +Firefox+ Librewolf devtools global
search in debugger tab (`C-F') I managed to find 2 key files:
Found with the keyword included in the selection request body,
this file exposes a class with these key functions:
- `select(body: Record)'
- `cancel(body: Record)'
Both call a method from
`this.seatsClient.(select|cancel)'. Interesting.
I configured a network override for this file in devtools and
duplicated the implementation of `cancel(body)' into
`select(body)', with the hope to get a cancellation when
selecting a seat. Didn't worked, but I realized the request
wasn't `POST' anymore, but `DELETE'.
I had to analyze further. Reading it again, I saw the both
functions defines the body this way:
,----
| // ...
| select(e) { // `e` is the `body`
| let r = { seatSelectRequest: l(S({}, e), { returnSession: !0 }) },
| // ...
`----
I assumed the function `S' is doing a deep copy of `e'
properties into a new object, a kind of a `Object.assign()'. So
it means the meaningful part of the body is built by the caller.
The caller. Found this by searching which files calls the
`cancel(body)' method.
It exposes a class named `o' with the method
`onSubmitRemoveSeatMap()', which calls the `cancel(body)'
function:
,----
| onSubmitRemoveSeatMap() {
| // ...
| this.seatsClientFacade
| .cancel({ passengerFlightIds: [this.getFlightIdFromPassenger()] })
| // ...
| }
`----
Bingo. This function is strangely not used across the bundles,
but luckly it was left there.
The `select(body)' payload is known because devtools network tab
logged it. Comparing both, it differ. Here's for setting:
[payload for setting the seat]:
,----
| {
| "seatSelectRequest": {
| "passengerFlightId": "",
| "seatNumber": "",
| "returnSession": true
| }
| }
`----
And the DTO for deleting which this file reveals:
,----
| {
| "seatsCancelRequest": {
| "passengerFlightIds": [""],
| "returnSession": true
| }
| }
`----
Now I have the:
- Method, URL and headers to call the `DELETE' route.
- Body built by a unused caller.
[Check-in data retrieval]
[Seat selection]
[payload for setting the seat]
The final query
===============
Finally I was able to call the `DELETE' to unset my selection. Because
it receives a (damn) Google ReCaptcha token and a rotating bearer
token, what I did was patch the `select(body)' method to build the
body from itself, so I can call `DELETE' when selecting a seat. The
following diff is all I did in [chunk-WC72Z7VP.js] bundle:
,----
| diff /tmp/chunk-WC72Z7VP.js /tmp/chunk-WC72Z7VP.new.js
`----
,----
| 95c95,100
| < let r = { seatSelectRequest: l(S({}, e), { returnSession: !0 }) },
| ---
| > let r = {
| > seatsCancelRequest: l(
| > S({}, { passengerFlightIds: [""] }),
| > { returnSession: !0 },
| > ),
| > },
| 99c104
| < .select(
| ---
| > .cancel(
| 111c116
| < reservation: i.result.response.seatSelectResponse.reservation,
| ---
| > reservation: i.result.response.seatCancelResponse.reservation,
`----
Once it worked, I reproduced it in `restclient.el' for clearance. I
ran with my current bearer and ReCaptcha and worked as well:
,----
| DELETE https://g2s-checkin-api.voegol.com.br/api/seats/cancel
| flow=Default
| User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:148.0) Gecko/20100101 Firefox/148.0
| Accept: application/json
| sessionId: :session-id
| Content-Type: application/json
| Authorization: Bearer :bearer
| Origin: https://b2c.voegol.com.br
|
| {
| "seatsCancelRequest": {
| "passengerFlightIds": [
| ":flight-id"
| ],
| "returnSession": true
| }
| }
`----
You can see it in my public restclient repo:
[chunk-WC72Z7VP.js]
Outro
=====
In the next time I'll take a increased care before clicking in any
selection, these websites offer no transparency and it's pretty
frustrating the user experience, forcing you to either finish the
check-in paying the seat selection, or having to arrive sooner in the
airport to do the check-in from there, hoping it won't take much time.
Keep aware, modern internet is dangerous :P
Footnotes
_________
[1]
[2] Which [seems wrong as far as I understood about].
------------------------------------------------------------------------
[HTML version] (/index.html)
Clone the [Git repo] (https://codeberg.org/thisago/blog)!
[Privacy Policy] (/meta/privacyPolicy.txt)
[About Site] (/meta/aboutSite.txt)
[Contact] (/meta/contact.txt)
[Sitemap] (/sitemap.txt)
Generated by Emacs