Cách Kiểm Tra Chu Kỳ Thanh Toán Stripe Trong 5 Phút
Nếu bạn đã từng triển khai một tính năng thanh toán và hy vọng nó "chỉ hoạt động", thì bài viết này dành cho bạn.
🧪 Câu Hỏi Tồn Tại Của Lập Trình Viên: "Làm Thế Nào Để Kiểm Tra Thanh Toán?"
Bạn vừa triển khai gói Pro với giá 250 USD/tháng và giai đoạn dùng thử 7 ngày. QA vào với câu hỏi đáng sợ:
"Vậy... làm thế nào để chúng ta thực sự kiểm tra toàn bộ vòng đời thanh toán?"
Và bạn đứng hình.
Hãy phân tích các lựa chọn của bạn:
- Chờ thực tế (7+ ngày, rồi thêm 30 ngày cho chu kỳ tiếp theo 😴)
- Chỉnh sửa timestamp trong cơ sở dữ liệu (hỏng, dễ bị lỗi webhook)
- Giả lập Stripe (nhanh nhưng thiếu hành vi thực tế)
- Triển khai và cầu nguyện (đừng giả vờ là bạn chưa bao giờ làm điều này 😅)
Không có lựa chọn nào là tốt cả. Chu kỳ thanh toán thực tế của Stripe cần thời gian thực, điều này không phù hợp cho CI/CD hoặc lặp lại nhanh.
Vì vậy, chúng tôi đã tìm ra một cách tốt hơn.
🧠 Tóm Tắt Ngắn Gọn – Công Nghệ Chúng Tôi Đã Sử Dụng
Chúng tôi đã xây dựng một bộ kiểm tra thanh toán nhanh và lặp lại bằng cách sử dụng:
- ✅ Stripe Test Clocks để mô phỏng thời gian
- ✅ Playwright cho toàn bộ trải nghiệm E2E (bao gồm UI + webhook)
- ✅ API backend để quản lý đồng hồ thử nghiệm một cách an toàn
- ✅ Trợ giúp kiểm tra TypeScript để điều khiển mọi thứ
Toàn bộ vòng đời thanh toán (thử nghiệm → thanh toán → gia hạn) được xác minh trong dưới 5 phút.
Cài Đặt Hệ Thống: Stripe Test Clocks + Playwright
Dưới đây là cách hệ thống được cấu trúc:
1. API Backend (Node.js / TypeScript)
- Tạo và nâng cấp Stripe Test Clocks một cách an toàn
- Hạn chế các thao tác chỉ cho môi trường
test/dev - Tổ chức việc tạo đăng ký + phối hợp webhook
2. Trợ Giúp Frontend
- Tiện ích TypeScript cho các bài kiểm tra
- Bọc các cuộc gọi API để tạo/nâng cấp đồng hồ
- Xác thực trạng thái đăng ký + hóa đơn
3. Kiểm Tra E2E Bằng Playwright
- Mô phỏng việc nâng cấp thực tế của người dùng qua UI
- Kích hoạt quy trình thanh toán Stripe
- Nâng cao thời gian + xác minh sự kiện thanh toán
🧩 Tại Sao Test Clocks Quan Trọng
Stripe's Test Clocks cho phép bạn:
- Tạo một phiên bản "sandbox" của thời gian
- Gắn các tài nguyên (khách hàng, đăng ký) vào đồng hồ đó
- Di chuyển thời gian tới (ví dụ: +7 ngày, +30 ngày)
- Quan sát cách hành vi thanh toán qua các ranh giới thời gian
Rất phù hợp cho CI, tái hiện lỗi hoặc xác thực các trường hợp biên phức tạp trong thanh toán.
🛠️ Điểm Nổi Bật Trong Mã: Cách Thức Hoạt Động
Tạo Một Test Clock
typescript
export const createTestClock = async (frozenTime: Date) => {
ensureTestEnvironment(); // An toàn là trên hết
return await stripe.testHelpers.testClocks.create({
frozen_time: Math.floor(frozenTime.getTime() / 1000),
name: `billing-test-${Date.now()}`,
});
};
✅ Chỉ được phép trong môi trường test/dev (chúng tôi thực thi điều này ở mọi lớp)
Nâng Cao Thời Gian & Chờ Webhooks
Việc nâng cao thời gian không đủ — bạn cũng cần chờ Stripe xử lý webhook:
typescript
export const advanceTestClockAndWait = async (
testClockId: string,
targetTime: string
) => {
await stripe.testHelpers.testClocks.advance(testClockId, {
frozen_time: Math.floor(new Date(targetTime).getTime() / 1000),
});
// Poll cho đến khi webhook được xử lý
await waitForWebhookProcessing(testClockId);
};
Logic Xác Minh Thanh Toán
Bạn sẽ muốn xác minh:
- Trạng thái đăng ký (đang thử nghiệm, đang hoạt động, v.v.)
- Hóa đơn được tạo + đã thanh toán
- Số tiền chính xác (hóa đơn dùng thử $0 so với $250 đang hoạt động)
typescript
export const verifyBillingLifecycle = async (
page: Page,
customerId: string,
subscriptionId: string
) => {
const response = await page.request.post('/api/v1/billing/test/verify', {
data: { customerId, subscriptionId },
});
return response.json();
};
🧪 Ví Dụ Thực Tế: Thử Nghiệm → Thanh Toán → Gia Hạn
Hãy cùng đi qua một bài kiểm tra vòng đời thanh toán đầy đủ trong Playwright.
1. Tạo một thời gian đông lạnh
typescript
const frozenTime = new Date();
frozenTime.setHours(0, 0, 0, 0);
2. Thiết lập môi trường
typescript
const testEnv = await createTestClockEnvironment(page, accountId, 'pro', 'monthly', frozenTime.toISOString());
3. Người dùng nâng cấp lên Pro qua UI
typescript
await subscriptionPage.upgradeToProPlan('monthly');
await completeStripeCheckout(page, STRIPE_TEST_CARDS.VISA_SUCCESS);
4. Xác minh trạng thái dùng thử
typescript
const result = await verifyBillingLifecycle(
page,
testEnv.customer.id,
testEnv.subscription.id,
);
expect(result.subscription.status).toBe('trialing');
5. Nâng cao 8 ngày (sau giai đoạn dùng thử)
typescript
await advanceTestClockAndWaitForWebhooks(
page,
testEnv.testClock.id,
addDays(frozenTime, 8).toISOString()
);
6. Xác minh khoản phí đầu tiên ($250)
typescript
const invoices = await verifyBillingLifecycle(...);
expect(invoices.total).toBeGreaterThan(0);
7. Nâng cao thêm 30 ngày → Thanh toán lần hai
typescript
await advanceTestClockAndWaitForWebhooks(
page,
testEnv.testClock.id,
addDays(frozenTime, 40).toISOString()
);
8. Xác minh khoản phí thứ hai ($250 nữa)
typescript
const updated = await verifyBillingLifecycle(...);
expect(updated.invoices.details.length).toBe(2);
Tổng thời gian kiểm tra: < 5 phút
Thời gian thực tế tương đương: 40+ ngày
⚠️ Bài Học Rút Ra (Từ Kinh Nghiệm)
✅ Cách Ly Môi Trường Là Quan Trọng
Chúng tôi đã thêm kiểm tra ensureTestEnvironment() vào mọi hàm. Bạn không muốn các đồng hồ thử nghiệm chạy trong môi trường sản xuất.
✅ Cần Xử Lý Webhook Bằng Cách Polling
Thời gian cố định không đủ. Chúng tôi đã xây dựng một hệ thống polling để kiểm tra tính sẵn sàng của webhook trước khi tiếp tục.
✅ Dọn Dẹp Hoặc Bị Lộn Xộn
Các đồng hồ thử nghiệm + đăng ký tích lũy rất nhanh. Chúng tôi tự động dọn dẹp khi tháo gỡ và cung cấp các điểm cuối dọn dẹp thủ công.
🚀 Tại Sao Điều Này Quan Trọng
Trước:
- Kiểm tra thủ công trong môi trường phát triển
- Không có cách nào để kiểm tra toàn bộ chu kỳ trước khi sản xuất
Sau:
- Thời gian kiểm tra nhanh hơn 99.99%
- Toàn bộ trải nghiệm E2E (dùng thử, phí, gia hạn)
- Các bài kiểm tra giống nhau chạy trong CI và địa phương
- Không có bất ngờ trong sản xuất
🧼 Dọn Dẹp Để Thành Công
Chúng tôi tự động hủy bỏ các đăng ký thử nghiệm và xóa các đồng hồ:
typescript
export const cleanupTestClockEnvironment = async (testClockId) => {
const subs = await getTestClockSubscriptions(testClockId);
for (const s of subs) await stripe.subscriptions.cancel(s.id);
await stripe.testHelpers.testClocks.del(testClockId);
};
Kết nối nó vào quá trình tháo gỡ kiểm tra của bạn:
typescript
test.afterAll(async () => {
await cleanupTestClockEnvironment(testEnvironment.testClock.id);
});
Suy Nghĩ Cuối Cùng: Kiểm Tra Thanh Toán Như Một Lập Trình Viên
Đây không phải là về việc làm đẹp các bài kiểm tra. Nó là về sự tự tin của lập trình viên.
Với Stripe Test Clocks + tự động hóa E2E, chúng tôi đã ngừng triển khai các tính năng thanh toán và cầu nguyện. Bây giờ chúng tôi biết toàn bộ vòng đời hoạt động trước khi bất kỳ điều gì đến sản xuất.
- An toàn cho CI
- Hoàn toàn xác định
- Xác thực chuyển động tiền thực tế
Nếu bạn đang xây dựng một sản phẩm đăng ký và không kiểm tra như thế này... bạn chắc chắn nên làm như vậy.
Chiến Lược Kiểm Tra Thanh Toán Của Bạn Là Gì?
Bạn có đang sử dụng test clocks? Giả lập mọi thứ? Vẫn đang chạy các bài kiểm tra thủ công trong giai đoạn triển khai?
Hãy chia sẻ ý kiến, để lại một bình luận về cách bạn đang xử lý điều này hôm nay.