@autobusal/order-confirm 1.2.2 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/OrderConfirm.tsx +45 -9
- package/package.json +5 -2
- package/services.ts +12 -1
- package/CHANGELOG.md +0 -80
package/OrderConfirm.tsx
CHANGED
|
@@ -4,7 +4,7 @@ import { useSearchParams, useParams, useNavigate, Navigate } from 'react-router-
|
|
|
4
4
|
import { AiOutlineCheck } from 'react-icons/ai';
|
|
5
5
|
import { BiErrorAlt } from 'react-icons/bi';
|
|
6
6
|
import { IoReturnUpBackOutline } from 'react-icons/io5';
|
|
7
|
-
import { Meta, Button } from '@autobusal/common';
|
|
7
|
+
import { Meta, Button, LoadingParagraph } from '@autobusal/common';
|
|
8
8
|
import Reason from './Reason';
|
|
9
9
|
import Telegram from './Telegram/Telegram';
|
|
10
10
|
import { Title, Actions } from './styles';
|
|
@@ -25,6 +25,21 @@ const escapeHtml = (value: string): string => (
|
|
|
25
25
|
}[char] as string))
|
|
26
26
|
);
|
|
27
27
|
|
|
28
|
+
/*
|
|
29
|
+
* What stands in for the confirmation sentence until the booking reference
|
|
30
|
+
* has been fetched.
|
|
31
|
+
*
|
|
32
|
+
* The page's own <Title> already says "reserved" / "purchased", so the
|
|
33
|
+
* outcome is on screen the whole time - only the reference is waited for,
|
|
34
|
+
* and the two lines sit in the same `.box` the sentence will land in, so
|
|
35
|
+
* nothing shifts when it arrives.
|
|
36
|
+
*/
|
|
37
|
+
const Waiting = (): JSX.Element => (
|
|
38
|
+
<div className="box">
|
|
39
|
+
<LoadingParagraph count={ 2 } />
|
|
40
|
+
</div>
|
|
41
|
+
);
|
|
42
|
+
|
|
28
43
|
// long enough to read the confirmation and its order number, short enough
|
|
29
44
|
// that nobody wonders whether the page has finished
|
|
30
45
|
const REDIRECT_AFTER = 5000;
|
|
@@ -40,9 +55,23 @@ const OrderConfirm = ({ t }: Props): (JSX.Element | null) => {
|
|
|
40
55
|
|
|
41
56
|
const { data: UserData } = useUserStore();
|
|
42
57
|
|
|
43
|
-
const { data: NumberData } = useGetOrderNumber(hash);
|
|
58
|
+
const { data: NumberData, isPending: numberPending } = useGetOrderNumber(hash);
|
|
44
59
|
|
|
45
|
-
|
|
60
|
+
/*
|
|
61
|
+
* Claude - 2026-08-31 (ruled by Ferjolt: "We should not show the internal
|
|
62
|
+
* hash anywhere but should show always the PNR").
|
|
63
|
+
*
|
|
64
|
+
* This used to fall back to `hash` when the lookup had not answered, which
|
|
65
|
+
* is how a live buyer came to see a 15-character Hashids token where the
|
|
66
|
+
* six-character booking reference belonged: the lookup was rate-limited
|
|
67
|
+
* during the "Too Many Attempts" episode, the fallback fired, and the page
|
|
68
|
+
* confidently printed an internal credential the visitor cannot type into
|
|
69
|
+
* "check your reservation" and cannot quote to anyone.
|
|
70
|
+
*
|
|
71
|
+
* The hash is no longer a fallback at all. It is not a booking reference,
|
|
72
|
+
* so it is never the answer to "what is my booking reference".
|
|
73
|
+
*/
|
|
74
|
+
const orderNumber = NumberData?.order_number ?? '';
|
|
46
75
|
|
|
47
76
|
/*
|
|
48
77
|
* Claude - 2026-08-22 (audit finding BOOK-11-b): this page is where the
|
|
@@ -140,12 +169,19 @@ const OrderConfirm = ({ t }: Props): (JSX.Element | null) => {
|
|
|
140
169
|
Hashids token as "your order with number ..." while the email, the
|
|
141
170
|
ticket and the invoice all named the six-character PNR - and the
|
|
142
171
|
PNR is what /viewtrip's own Order Number field accepts and what a
|
|
143
|
-
support desk can look up.
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
172
|
+
support desk can look up.
|
|
173
|
+
|
|
174
|
+
2026-08-31: the sentence waits for the number rather than filling
|
|
175
|
+
the gap with the hash. It is one short GET on a page that then sits
|
|
176
|
+
for five seconds before redirecting, so the wait is not felt - and
|
|
177
|
+
announcing the wrong reference is worse than announcing it a moment
|
|
178
|
+
later. `order` is bolded because the visitor is expected to WRITE
|
|
179
|
+
IT DOWN; it is the only thing on the page they need to keep. */ }
|
|
180
|
+
{ numberPending ? <Waiting /> : (
|
|
181
|
+
<div className="box" dangerouslySetInnerHTML={{
|
|
182
|
+
__html: t(`order_confirm.content.${ type === 'cancelled' && released ? 'released' : type }`, { order: `<strong>${ escapeHtml(orderNumber) }</strong>` })
|
|
183
|
+
}} />
|
|
184
|
+
) }
|
|
149
185
|
|
|
150
186
|
<>
|
|
151
187
|
{ (type === 'cancelled' && message !== '') && <Reason message={ message } t={ t } /> }
|
package/package.json
CHANGED
package/services.ts
CHANGED
|
@@ -50,7 +50,18 @@ export const useGetOrderNumber = (hash?: string): UseQueryResult<{ order_number:
|
|
|
50
50
|
useQuery({
|
|
51
51
|
queryKey: ['order-number', hash],
|
|
52
52
|
enabled: hash !== undefined && hash !== '',
|
|
53
|
-
|
|
53
|
+
|
|
54
|
+
/*
|
|
55
|
+
* Claude - 2026-08-31: this is the one query on the page that MUST answer.
|
|
56
|
+
* It was `retry: false`, and a single rate-limited response was enough to
|
|
57
|
+
* lose the buyer their booking reference on the page that exists to give
|
|
58
|
+
* it to them - which is how a live buyer was shown the internal hash.
|
|
59
|
+
*
|
|
60
|
+
* Retrying is safe here in a way it is not for the payment calls in the
|
|
61
|
+
* same file: this is a read, it creates nothing, and repeating it cannot
|
|
62
|
+
* duplicate an order the way a repeated initiation could.
|
|
63
|
+
*/
|
|
64
|
+
retry: 3,
|
|
54
65
|
queryFn: async () => (
|
|
55
66
|
await apiClient
|
|
56
67
|
.get('/api/orders/number', { params: { hash } })
|
package/CHANGELOG.md
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
## 1.2.2 - 2026-08-29
|
|
4
|
-
|
|
5
|
-
- The purchase confirmation shows the real PNR rather than the internal hash from its own URL (audit A3-03h) - the email, the ticket and the invoice all name the PNR, and it is the code /viewtrip and a support desk accept. Falls back to the hash, so the page can always confirm.
|
|
6
|
-
|
|
7
|
-
## 1.2.1
|
|
8
|
-
|
|
9
|
-
### Fixed
|
|
10
|
-
|
|
11
|
-
- **The cancelled-payment page tells the truth.** It used to say "your order has been cancelled" while the reservation kept holding its seats for two more hours - and the gateway's failUrl lands on the same page, so auto-cancelling would kill declined-card retries. New copy (payment not completed, nothing charged, reservation still held), a Try-again action, and a real "Release my reservation" button through the hash-authorised cancel endpoint. (Audit BOOK-11-b.)
|
|
12
|
-
|
|
13
|
-
## 1.2.0
|
|
14
|
-
|
|
15
|
-
- Announces `purchase` on arrival, for a **purchased** order only — a reservation is not revenue and a cancelled attempt certainly is not. Cleared as it fires, so refreshing this page (a plain URL with the buyer's order number on it) cannot report the same sale twice.
|
|
16
|
-
|
|
17
|
-
All notable changes to this package are documented here. This project follows
|
|
18
|
-
[Keep a Changelog](https://keepachangelog.com/) and [Semantic Versioning](https://semver.org/).
|
|
19
|
-
|
|
20
|
-
## [1.1.3] - 2026-08-01
|
|
21
|
-
|
|
22
|
-
### Changed
|
|
23
|
-
|
|
24
|
-
- **The confirmation moves on to the order after five seconds**, where the
|
|
25
|
-
tickets and the invoice actually are. A dead-end success page turned
|
|
26
|
-
downloading the thing you had just bought into a hunt. Only for purchased
|
|
27
|
-
and reserved: a cancellation has nothing to go to, and bouncing someone
|
|
28
|
-
off a notice they may still be reading would be its own small insult.
|
|
29
|
-
Cleared on unmount, so pressing a button first does not leave a second
|
|
30
|
-
navigation queued behind the one you chose.
|
|
31
|
-
|
|
32
|
-
## [1.1.2] - 2026-07-30
|
|
33
|
-
|
|
34
|
-
### Fixed
|
|
35
|
-
|
|
36
|
-
- **`noIndex` never used on the order confirmation page (`OrderConfirm.tsx`).**
|
|
37
|
-
Reserved/purchased/cancelled order confirmations are reachable by anyone
|
|
38
|
-
with the emailed link, no login required, and each is single-use - there is
|
|
39
|
-
no canonical indexable version. `<Meta>`'s `noIndex` prop existed but was
|
|
40
|
-
never passed anywhere in the app; it now is here.
|
|
41
|
-
|
|
42
|
-
## [1.1.1] - 2026-07-25
|
|
43
|
-
|
|
44
|
-
### Fixed
|
|
45
|
-
|
|
46
|
-
- The "Connect Telegram" addon widget now also renders on `reserved` order
|
|
47
|
-
confirmations, not just `purchased` ones - a reserve-then-pay-later
|
|
48
|
-
booking has no other page offering the binding link, so it was a dead
|
|
49
|
-
end for the Telegram addon on that path.
|
|
50
|
-
|
|
51
|
-
## [1.1.0] - 2026-07-25
|
|
52
|
-
|
|
53
|
-
### Added
|
|
54
|
-
|
|
55
|
-
- **Post-purchase Telegram addon binding.** When a purchased order has a
|
|
56
|
-
Telegram notification addon still awaiting binding, the "purchased"
|
|
57
|
-
confirmation page now shows a "Connect Telegram" deep link (polls obtapi's
|
|
58
|
-
`GET /api/orders/telegram/status?hash=...` every 4s until bound). Mirrors
|
|
59
|
-
magus's account-level `AccountTelegram` binding flow, but order-scoped
|
|
60
|
-
since a guest checkout has no account to bind against.
|
|
61
|
-
|
|
62
|
-
## [1.0.1] - 2026-07-19
|
|
63
|
-
|
|
64
|
-
### Security
|
|
65
|
-
|
|
66
|
-
- `OrderConfirm.tsx`: fixed an HTML-injection (XSS) vector. The `hash` segment
|
|
67
|
-
comes straight from the URL (`/orders/:type/:hash`) and was interpolated into
|
|
68
|
-
markup rendered via `dangerouslySetInnerHTML`. Added an `escapeHtml` helper
|
|
69
|
-
that encodes `& < > " '` and applied it to the hash before interpolation
|
|
70
|
-
(`<strong>${ escapeHtml(String(hash ?? '')) }</strong>`), so a crafted hash
|
|
71
|
-
(e.g. containing `<img onerror=...>`) can no longer inject markup.
|
|
72
|
-
|
|
73
|
-
### Fixed
|
|
74
|
-
|
|
75
|
-
- `OrderConfirm.tsx`: the "Manage order" action now routes based on auth state.
|
|
76
|
-
A logged-in user is sent to `/account/orders` (where the order is listed)
|
|
77
|
-
instead of the public `/viewtrip/:hash` lookup form that email-gates access to
|
|
78
|
-
their own order; guests still use the public `/viewtrip/:hash` lookup.
|
|
79
|
-
|
|
80
|
-
Authored by Ferjolt Ozuni. Consolidated from the magus and alvavel whitelabel patch sets into canonical @autobusal source (eliminates per-repo patch-package divergence).
|