외부 서비스 웹훅을 사용자가 보고 있는 페이지에 보여 주기
결제 확인, 배송 이동, 빌드 완료: Stripe, 택배사, GitHub 웹훅을 사용자가 보고 있는 페이지로 새로고침 없이 전달합니다.
고객이 Stripe Checkout에서 결제하고 '감사합니다' 페이지로 돌아와도, 결제를 확정해 주는 웹훅은 몇 초 늦게 도착할 수 있습니다. 페이지에는 '결제를 확인하는 중…'이 떠 있고, 고객은 새로고침하거나 심지어 한 번 더 결제합니다. 택배사, CI 서비스, 전자서명 도구, 마켓플레이스도 마찬가지입니다. 소식은 서버에 도착할 뿐, 사용자가 보고 있는 페이지에는 오지 않습니다. Hibira를 쓰면 웹훅 핸들러가 받는 즉시 그 업데이트를 페이지로 전달합니다.
사용자가 얻는 것
- 보고 있던 페이지에 새로고침 없이 '결제 완료'가 표시됨.
- 배송, 빌드, 서명 진행 상황이 저절로 나타남.
- 첫 결제가 실패한 줄 알고 다시 결제하는 일이 없어짐.
사용하는 기능
- 주문한 본인만 참여할 수 있는 비공개 채널(
private-order-42). 인가 엔드포인트에서 확인합니다. - 제공자의 서명을 검증하고 변경을 저장하는 바로 그 웹훅 핸들러에서 호출하는
trigger. - 현재 상태를 한 번 읽어 오는
pusher:subscription_succeeded. 페이지가 열리기 전에 도착한 웹훅도 놓치지 않습니다.
1단계: 저장한 뒤 웹훅 전달하기
서명을 검증하고, 이미 처리한 이벤트는 건너뛰고(제공자가 재시도합니다), 새 상태를 저장한 다음 발행합니다:
app.post('/stripe/webhook', express.raw({ type: 'application/json' }), async (req, res) => {
const event = stripe.webhooks.constructEvent(req.body, req.headers['stripe-signature'], process.env.STRIPE_WEBHOOK_SECRET);
if (await db.webhookEvents.seen(event.id)) return res.sendStatus(200);
if (event.type === 'checkout.session.completed') {
const orderId = event.data.object.metadata.orderId;
await db.orders.update(orderId, { status: 'paid' });
await pusher.trigger(`private-order-${orderId}`, 'status', { status: 'paid' });
}
res.sendStatus(200);
});2단계: 본인만 들을 수 있게 하기
누가 구독할 수 있는지는 채널 인가 엔드포인트가 정합니다:
app.post('/pusher/auth', requireLogin, express.urlencoded({ extended: false }), async (req, res) => {
const id = req.body.channel_name.match(/^private-order-(.+)$/)?.[1];
if (!id || !(await db.orders.ownedBy(id, req.user.id))) return res.sendStatus(403);
res.json(pusher.authorizeChannel(req.body.socket_id, req.body.channel_name));
});3단계: 페이지에서 듣고, 상태를 한 번 읽기
먼저 구독하고, 구독이 준비되면 현재 상태를 한 번 읽습니다. 그러면 웹훅이 페이지보다 먼저 오든 나중에 오든 상관없습니다:
const channel = pusher.subscribe('private-order-42');
channel.bind('status', ({ status }) => show(status));
channel.bind('pusher:subscription_succeeded', async () => {
const order = await fetch('/api/orders/42').then((r) => r.json());
show(order.status);
});다른 웹훅도 같은 방식으로
| 출처 | 웹훅 | 열린 페이지에 보이는 것 |
|---|---|---|
| Stripe, PayPal, Adyen | 결제 성공 또는 실패 | 영수증, 또는 다시 시도 버튼 |
| 택배사 | 배송 추적 업데이트 | 주문 페이지의 새 단계 |
| GitHub, GitLab, Vercel | 빌드 또는 배포 완료 | 상태 배지와 미리보기 링크 |
| 전자서명 도구 | 문서 서명 완료 | '양측 서명 완료' |
| Shopify, 마켓플레이스 | 주문 생성 또는 결제 | 판매자 대시보드에 새 행 |