# Lên production

Khi tích hợp đã chạy trọn luồng ở `development`, chỉ còn vài bước để chuyển sang production: liên kết tài khoản ngân hàng thật, kết nối tài khoản hóa đơn thật, đổi key và môi trường, đảm bảo handler chặt chẽ, rồi test bằng một giao dịch thật nhỏ. Trang này đi qua từng bước và kết thúc bằng những gì cần theo dõi khi đã chạy thật.

## Trước khi bắt đầu

Hãy chắc chắn toàn bộ luồng chạy được ở development:

- Payment tạo từ trang thanh toán của bạn chuyển sang `paid` sau khi [giả lập chuyển khoản](https://hatang.dev/vi/docs/guides/payments/environments.md#simulate-a-transfer).
- Handler IPN xác minh chữ ký, cập nhật đơn đúng một lần, và trả `2xx`.
- Trả thiếu, trả thừa, hết hạn và tiền về muộn được xử lý đúng như bạn muốn (xem [kịch bản test](https://hatang.dev/vi/docs/guides/payments/environments.md)).
- Nếu bạn phát hành hóa đơn, một hóa đơn sandbox đã tới `issued`.

Không có gì được chuyển giữa hai môi trường: tài khoản ngân hàng, cấu hình khớp mã, payment, nhà cung cấp hóa đơn và khoá tích hợp đều phải có ở production.

## 1. Liên kết tài khoản ngân hàng thật

Chuyển dashboard sang production (thanh môi trường trên cùng các trang Thanh toán, **Chuyển sang chính thức**), rồi mở **Cài đặt thanh toán → Tài khoản ngân hàng → Thêm tài khoản ngân hàng** và chọn ngân hàng:

| Ngân hàng | Bạn cần |
|---|---|
| MB Bank | Số tài khoản, tên chủ tài khoản đúng như MB ghi nhận, và CCCD hoặc mã số thuế dùng khi mở tài khoản. MB nhắn mã tới số điện thoại đăng ký của chủ tài khoản |
| BIDV | Đăng ký dịch vụ thông báo biến động số dư của BIDV cho tài khoản tại BIDV, rồi thêm tài khoản. Tài khoản kích hoạt khi BIDV gửi tin đầu tiên |
| ACB | Số tài khoản. Sau đó chủ tài khoản đồng ý trên trang của ACB; sự đồng ý có hiệu lực tối đa 180 ngày |
| Pay2S | Tài khoản đã liên kết trên Pay2S, một Hook trỏ tới URL hiển thị trong form, và token của Hook |

Chi tiết ở [Liên kết ngân hàng](https://hatang.dev/vi/docs/guides/payments/bank-connections.md). Sau đó đặt lại **cấu hình khớp mã** cho tài khoản production: tiền tố mã thanh toán, định dạng mã đơn của bạn, và những giao dịch `transaction.received` sẽ báo.

> **Caution**
>
> Người nhận mã của ngân hàng hoặc đồng ý trên trang ngân hàng phải là chủ tài khoản, hoặc người được họ ủy quyền. Hãy chọn thời điểm liên kết khi họ có mặt.

## 2. Kết nối nhà cung cấp hóa đơn điện tử

Nếu bạn phát hành hóa đơn, mở **Cài đặt thanh toán → Nhà cung cấp hoá đơn** ở production và kết nối tài khoản Mắt Bão thật: mã số thuế, tài khoản và mật khẩu API trong mục "Cấu hình API" trên cổng Mắt Bão. Chọn mẫu số và ký hiệu đã đăng ký cho doanh nghiệp, và chọn điều gì xảy ra khi đơn đã thanh toán yêu cầu hóa đơn: dừng ở nháp, hay gửi ký luôn.

Sau đó gửi **callback URL** của kết nối cho bộ phận hỗ trợ Mắt Bão, để thay đổi phía họ về Hạ Tầng ngay. Xem [Hóa đơn điện tử](https://hatang.dev/vi/docs/guides/payments/einvoices.md).

## 3. Đổi key và môi trường

| Cấu hình | Development | Production |
|---|---|---|
| Môi trường | `Hatang-Environment: development` hoặc SDK `environment: "development"` | Bỏ header và tùy chọn (hoặc gửi `production`) |
| Secret key cho IPN | `sk_test_…` | `sk_…` tại **Cài đặt thanh toán → Khoá tích hợp** ở production |
| Webhook secret | `whsec_…` | Cùng một endpoint phục vụ cả hai môi trường; giữ nguyên secret |
| API key | `ht_…` có `payments:read`, `payments:write` | Dùng được cùng key; có thể tạo key riêng cho từng server |

Giữ các giá trị này trong cấu hình, không viết vào code:

```bash
HATANG_API_KEY=ht_123...
HATANG_SECRET_KEY=sk_123...         # secret key production, cho IPN
HATANG_WEBHOOK_SECRET=whsec_123...  # secret của webhook endpoint
# Không đặt HATANG_ENVIRONMENT ở production
```

## 4. Kiểm tra endpoint

- `ipn_url` và webhook endpoint là **https** trên internet công khai, trả `2xx` trong 10 giây (15 giây với webhook), và không redirect.
- Endpoint đọc **body gốc** để xác minh (không có middleware JSON chạy trước).
- Webhook endpoint đã đăng ký các sự kiện bạn cần, ví dụ `payment.succeeded`, `payment.overpaid`, `transaction.received`, `bank_connection.revoked`, `invoice.failed`.
- Tin vào chữ ký, không tin vào địa chỉ IP của bên gọi.

## 5. Siết chặt handler

Handler của bạn phải:

1. Xác minh chữ ký bằng key **production**, và từ chối mọi lời gọi không hợp lệ.
2. Kiểm tra `environment` là `production`; payment development không bao giờ được mở khóa đơn thật.
3. So `paid_amount` với tổng tiền của đơn.
4. Chống trùng theo `id` của IPN hoặc `svix-id` của webhook.
5. Chỉ đưa đơn tiến lên, bất kể lời gọi đến theo thứ tự nào.

Trước khi giao hàng cho người mua quay lại `return_url`, hãy xác nhận ở server bằng `GET /payments/{id}` hoặc `payments.verify()` của SDK.

## 6. Test bằng giao dịch thật nhỏ

1. Tạo payment ở production với số tiền nhỏ, ví dụ 10.000 VND, kèm `ipn_url` thật.
2. Trả bằng app ngân hàng của bạn bằng cách quét QR.
3. Kiểm tra payment chuyển sang `paid` (với ACB, chờ tới 10 phút vì Hạ Tầng đọc lịch sử theo chu kỳ đó), IPN đã tới (`GET /payments/{id}/ipn-deliveries`), và giao dịch có trong **Thanh toán → Giao dịch**.
4. Nếu đơn có yêu cầu hóa đơn, kiểm tra hóa đơn tới `issued` và có số.
5. Sau đó test tình huống sai: trả một payment nhỏ khác mà không ghi mã, và kiểm tra `transaction.received` đến với `payment_id: null`.

Tiền về chính tài khoản của bạn; không cần hoàn tiền qua Hạ Tầng.

## 7. Theo dõi khi đã chạy thật

| Cần theo dõi | Ở đâu |
|---|---|
| Lời gọi IPN lỗi liên tục | `GET /payments/{id}/ipn-deliveries` (`status: "exhausted"`), và trang payment |
| Webhook gửi lỗi | `GET /webhooks/{id}/deliveries`, và **Webhooks** trên dashboard |
| Tài khoản ngân hàng ngừng hoạt động | Webhook `bank_connection.revoked` và `bank_connection.needs_reauth`; trạng thái và giao dịch gần nhất của tài khoản tại **Tài khoản ngân hàng** |
| Sự đồng ý của ACB sắp hết hạn | `consent.expires_at` của kết nối; liên kết lại trước thời điểm đó |
| Lệnh gọi tới ngân hàng hoặc nhà cung cấp hóa đơn bị kẹt | **Thanh toán → Tình trạng tích hợp** |
| Hóa đơn lỗi hoặc bị từ chối | Webhook `invoice.failed`, `invoice.tax_rejected`, và email cảnh báo Hạ Tầng gửi cho chủ sở hữu và quản trị viên |
| Tiền vào chưa có hóa đơn | **Thanh toán → Đối soát hoá đơn**, và email hằng ngày lúc 08:00 |
| Giao dịch ngân hàng không báo về | [Nhập sao kê](https://hatang.dev/vi/docs/guides/payments/bank-connections.md#statement-import) cho những ngày bị thiếu |

## Danh sách kiểm tra

- [ ] Tài khoản ngân hàng thật đã liên kết và `active` ở production.
- [ ] Đã đặt cấu hình khớp mã cho tài khoản production.
- [ ] Đã kết nối nhà cung cấp hóa đơn ở production với đúng mẫu, và Mắt Bão đã có callback URL.
- [ ] Đã cấu hình **secret key production** và webhook secret trên server.
- [ ] Đã bỏ header `Hatang-Environment: development` (hoặc tùy chọn của SDK) ở production.
- [ ] Handler kiểm tra chữ ký, `environment`, số tiền, chống trùng, và chỉ đưa đơn tiến lên.
- [ ] URL IPN và webhook là `https` công khai, trả `2xx` trong 10 giây.
- [ ] Một giao dịch thật nhỏ đã chạy trọn luồng.
- [ ] Có người theo dõi lần gửi lỗi, sự kiện `bank_connection.*` và cảnh báo hóa đơn.

## Bước tiếp theo

- [Nhận kết quả](https://hatang.dev/vi/docs/guides/payments/payment-results.md).
- [Liên kết ngân hàng](https://hatang.dev/vi/docs/guides/payments/bank-connections.md).
- [Lỗi](https://hatang.dev/vi/docs/reference/errors.md) và [Giới hạn](https://hatang.dev/vi/docs/reference/limits.md).

Source: https://hatang.dev/vi/docs/guides/payments/go-live
