Chào anh em! Shop bán bằng WooCommerce mà gửi hàng qua Giao Hàng Tiết Kiệm (GHTK) thì kiểu gì cũng tới lúc ngán ba việc lặp đi lặp lại: khách hỏi phí ship thì mở trang GHTK tra tay, có đơn thì chép tên, số điện thoại, địa chỉ sang GHTK, giao xong lại quay về web đổi trạng thái đơn. Vài đơn một ngày thì còn chịu được, vài chục đơn là mất trắng một buổi.

Tích hợp Giao Hàng Tiết Kiệm vào WordPress là để website tự lo mấy việc đó. Đọc xong bài, anh em chọn được nên tự code bằng API của GHTK hay dùng plugin, và biết chỗ cần kiểm khi phí ship không hiện hay trạng thái đơn không về.
Plugin ở cách 2 do mình viết và hỗ trợ từ năm 2018, anh em nhớ điều này khi đọc phần cân giữa tự code và dùng plugin. Chỗ nào nói về API, mình theo tài liệu chính thức của GHTK; chỗ nào nói về plugin, mình mở mã nguồn ra đối chiếu.
Để duy trì blog nên mình có làm aff cho 1 số bên hosting. Nhưng dù aff mình cũng chọn 1 số nhà cung cấp uy tín về chất lượng và support nên các bạn cứ yên tâm nhé.
Nếu có mua hosting mà có trong list dưới đây các bạn click vào link trước khi mua để ủng hộ mình nhé. Mình cảm ơn nhiều
- Azdigi: Giá rẻ thì dùng gói Pro Gold Hosting còn chất lượng hơn thì em khuyên dùng Business Hosting. Có điều kiện thì lên VPS nhé
- Tino hosting với mã giảm giá độc quyền lên tới 40%. Xem tại đây
- iNet
- Nước ngoài thì Vultr
Tích hợp GHTK vào WordPress gồm những phần nào?
Một bộ kết nối đầy đủ có năm phần, phí ship ở trang thanh toán chỉ là phần đầu, và không phải shop nào cũng cần cả năm:
- Khách chọn địa chỉ ở trang thanh toán thì website hỏi GHTK rồi hiện đúng phí ship, thay vì một mức đồng giá cho cả nước.
- Đăng đơn là gửi người nhận, tiền thu hộ, khối lượng sang GHTK để nhận mã vận đơn, không phải gõ lại lần nào.
- Đồng bộ trạng thái là GHTK báo về website (qua webhook) mỗi khi vận đơn đổi trạng thái, đơn giao xong tự chuyển sang hoàn thành.
- In nhãn để dán lên gói hàng và gửi mã vận đơn cho khách tự tra.
- Ô địa chỉ khớp với GHTK là phần hay bị bỏ quên, vì WooCommerce mặc định để khách gõ địa chỉ tự do, trong khi GHTK cần tên tỉnh và phường/xã chuẩn mới tính được phí.
Shop nhỏ thường chỉ cần hai phần đầu là đã bỏ được phần lớn việc tay. Đồng bộ trạng thái đáng làm khi mỗi ngày có từ vài chục đơn trở lên.
Chuẩn bị trước khi kết nối
Dù tự code hay dùng plugin, anh em cần có sẵn mấy thứ này:
- Một tài khoản GHTK dạng cửa hàng đã đăng ký địa chỉ kho lấy hàng, vì GHTK chỉ tới lấy hàng ở địa chỉ đã khai với họ.
- API Token, lấy bằng cách đăng nhập trang khách hàng GHTK (khachhang.giaohangtietkiem.vn), vào Thông tin shop → Cấu hình API, bấm Tạo Token, đặt tên, chọn ngày hết hạn và các quyền cần dùng (tạo đơn, tính phí, lấy trạng thái…).
- Mã shop dạng chữ S kèm một dãy số, thấy ngay khi đăng nhập GHTK, đi kèm token trong mỗi request và dùng khi đăng ký webhook.
- Cân nặng cho từng sản phẩm, vì GHTK tính phí theo khối lượng và sản phẩm để trống cân nặng là phí sai hoặc không ra phí.
- Website chạy trên tên miền thật nếu muốn dùng webhook, vì GHTK phải gọi vào được từ Internet, website trên máy local thì không nhận được gì.
⚠️ Token là mật khẩu. Ai cầm token là tạo được vận đơn dưới tên shop của anh em. Chụp màn hình nhờ hỗ trợ thì che ô token lại, lỡ lộ thì vào GHTK tạo token mới. Token cũng có ngày hết hạn do chính anh em chọn lúc tạo: tới ngày đó mà quên đổi là phí ship biến mất khỏi trang thanh toán.
Muốn tập trước mà không tạo vận đơn thật thì GHTK có môi trường thử nghiệm riêng: đăng ký tài khoản tại khachhang-staging.ghtklab.com, token ở đó khác token thật.
Cách 1: Tự code bằng API của GHTK
GHTK có bộ API công khai, tài liệu ở api.ghtk.vn. Mọi request gửi tới https://services.giaohangtietkiem.vn (môi trường thử nghiệm là https://services-staging.ghtklab.com), kèm hai header: Token là API token, X-Client-Source là mã shop. Token sai hoặc hết hạn thì GHTK trả về lỗi 403.
Năm endpoint anh em sẽ dùng thường xuyên:
- Tính phí ship dùng
GET /services/shipment/fee. - Đăng đơn dùng
POST /services/shipment/order. - Xem trạng thái vận đơn dùng
GET /services/shipment/v2/{mã vận đơn}. - Huỷ vận đơn dùng
POST /services/shipment/cancel/{mã vận đơn}. - In nhãn dùng
GET /services/label/{mã vận đơn}.
Ví dụ: lấy phí ship GHTK trong WordPress
Đoạn dưới hỏi GHTK phí giao một gói hàng theo địa chỉ 2 cấp (tỉnh và phường/xã). Token, mã shop và địa chỉ kho đặt thành hằng số trong wp-config.php, đừng viết thẳng vào code.
/**
* Hỏi GHTK phí giao hàng theo địa chỉ 2 cấp (tỉnh + phường/xã).
*
* @param string $province Tỉnh/thành của khách.
* @param string $ward Phường/xã của khách.
* @param int $weight Khối lượng gói hàng, tính bằng gram.
* @param int $value Giá trị hàng hoá, dùng để tính phí bảo hiểm.
* @return int|WP_Error Phí ship (đồng) hoặc lỗi.
*/
function devvn_ghtk_get_fee( $province, $ward, $weight, $value = 0 ) {
$query = array(
'pick_province' => DEVVN_GHTK_PICK_PROVINCE,
'pick_ward' => DEVVN_GHTK_PICK_WARD,
'province' => $province,
'ward' => $ward,
'weight' => $weight,
'value' => $value,
'transport' => 'road',
);
$cache_key = 'devvn_ghtk_fee_' . md5( wp_json_encode( $query ) );
$fee = get_transient( $cache_key );
if ( false !== $fee ) {
return $fee;
}
$response = wp_remote_get(
'https://services.giaohangtietkiem.vn/services/shipment/fee?' . http_build_query( $query ),
array(
'timeout' => 10,
'headers' => array(
'Token' => DEVVN_GHTK_TOKEN,
'X-Client-Source' => DEVVN_GHTK_SHOP_CODE,
),
)
);
if ( is_wp_error( $response ) ) {
return $response;
}
$body = json_decode( wp_remote_retrieve_body( $response ), true );
if ( empty( $body['success'] ) || empty( $body['fee']['delivery'] ) ) {
return new WP_Error( 'ghtk_fee', isset( $body['message'] ) ? $body['message'] : 'GHTK không trả về phí' );
}
$fee = (int) $body['fee']['fee'];
set_transient( $cache_key, $fee, HOUR_IN_SECONDS );
return $fee;
}
Ba chỗ trong đoạn này mình thấy người mới hay làm sai:
- Dùng
http_build_query()để mã hoá tên tỉnh, phường có dấu và khoảng trắng, vì hàmadd_query_arg()của WordPress không mã hoá giá trị và GHTK sẽ nhận một chuỗi hỏng. - Kiểm thêm trường
fee.delivery(ngoàisuccess) để biết GHTK có giao tới địa chỉ đó hay không. - Cache kết quả, vì mỗi lần khách đổi địa chỉ WooCommerce lại tính phí, không cache là mỗi lần một request sang GHTK và trang thanh toán chậm theo.
Hàm này mới trả về một con số. Muốn khách thấy ở trang thanh toán, anh em còn phải viết một phương thức vận chuyển (lớp kế thừa WC_Shipping_Method) gọi nó trong calculate_shipping(), và trang thanh toán phải có ô chọn tỉnh, phường/xã đúng tên GHTK dùng.
Nhận trạng thái từ GHTK qua webhook
Webhook là chiều ngược lại: mỗi lần vận đơn đổi trạng thái, GHTK gửi một request POST về đường dẫn của anh em, kèm mã vận đơn (label_id), mã đơn bên anh em (partner_id), mã trạng thái (status_id), lý do, khối lượng và phí. Endpoint phải trả về HTTP 200, không thì GHTK gửi lại thêm một lần. Đường dẫn này ai biết cũng gọi được, nên phải có mã bí mật để chặn request giả.
add_action( 'rest_api_init', 'devvn_ghtk_register_webhook' );
/**
* Đăng ký đường dẫn nhận webhook: /wp-json/devvn/v1/ghtk-webhook?hash=MA_BI_MAT
*
* @return void
*/
function devvn_ghtk_register_webhook() {
register_rest_route(
'devvn/v1',
'/ghtk-webhook',
array(
'methods' => 'POST',
'callback' => 'devvn_ghtk_handle_webhook',
'permission_callback' => '__return_true',
)
);
}
/**
* Cập nhật đơn WooCommerce theo trạng thái GHTK gửi về.
*
* @param WP_REST_Request $request Request từ GHTK (body JSON hay form đều đọc được).
* @return WP_REST_Response
*/
function devvn_ghtk_handle_webhook( WP_REST_Request $request ) {
if ( ! hash_equals( DEVVN_GHTK_WEBHOOK_HASH, (string) $request->get_param( 'hash' ) ) ) {
return new WP_REST_Response( null, 403 );
}
$order = wc_get_order( absint( $request->get_param( 'partner_id' ) ) );
if ( $order ) {
$status_id = (int) $request->get_param( 'status_id' );
$reason = sanitize_text_field( (string) $request->get_param( 'reason' ) );
$order->add_order_note( sprintf( 'GHTK báo trạng thái %d: %s', $status_id, $reason ) );
if ( 5 === $status_id ) {
$order->update_status( 'completed' );
}
}
return new WP_REST_Response( null, 200 );
}
Mã trạng thái 5 là “Đã giao hàng”. Đường dẫn webhook thì không tự khai trên trang GHTK được: theo những lần mình hỗ trợ khách, phải dùng email đã đăng ký tài khoản GHTK gửi tới b2c@ghtk.vn kèm mã shop và đường dẫn, hoặc nhờ nhân viên chăm sóc riêng của shop bật giúp.
Những phần còn lại anh em phải tự làm
- Phương thức vận chuyển và ô địa chỉ phải có dữ liệu khớp GHTK. Từ 01/07/2025 cả nước bỏ cấp huyện, còn 34 tỉnh thành, nên bộ dữ liệu địa chỉ phải làm lại theo 2 cấp. API phí của GHTK giờ chỉ bắt buộc tỉnh và phường/xã, quận/huyện để trống được.
- Màn hình đăng đơn trong trang quản trị để chọn kho, ai trả phí ship, tiền thu hộ, các nhãn như cho xem hàng, hàng dễ vỡ. Màn hình này phải chặn cả đăng trùng, vì GHTK báo lỗi
ORDER_ID_EXISTkhi mã đơn đã gửi trước đó. - Bảng ánh xạ trạng thái, vì GHTK có hơn hai mươi trạng thái còn WooCommerce chỉ có vài cái.
- Xử lý lúc GHTK chậm hoặc lỗi để khách vẫn đặt được hàng, thay vì trang thanh toán treo.
- In nhãn, gửi email báo mã vận đơn và xử lý trường hợp có nhiều kho lấy hàng.
Làm cho chạy được bản đầu không khó. Cái tốn công là giữ nó chạy đúng qua mỗi lần GHTK đổi API hay dữ liệu địa chỉ thay đổi, như đợt sáp nhập tỉnh vừa rồi.
Cách 2: Dùng plugin GHTK cho WooCommerce
Không muốn tự viết và tự bảo trì những thứ trên thì dùng plugin để kết nối GHTK với WooCommerce. Plugin mình lấy làm ví dụ là plugin GHTK cho WooCommerce: làm đủ năm phần ở đầu bài, kèm luôn ô chọn tỉnh/thành, phường/xã cho trang thanh toán. Website cần WooCommerce 5.6 trở lên và hosting bật ionCube Loader. Tài liệu ghi thời gian cài đặt khoảng 30 phút, gồm 4 bước bắt buộc:
- Kiểm tra ionCube rồi tải file zip của plugin lên như mọi plugin khác. Xem hướng dẫn cài đặt.
- Kích hoạt license ở tab License. Xem kích hoạt bản quyền.
- Nhập token, mã shop và địa chỉ kho lấy hàng ở tab Cài đặt GHTK. Xem kết nối tài khoản GHTK.
- Kiểm tra phí ship bằng cách thêm một sản phẩm có cân nặng vào giỏ rồi chọn địa chỉ ở trang thanh toán. Phí GHTK tự hiện, không phải thêm vào khu vực giao hàng như các phương thức thường. Xem phí vận chuyển GHTK.

Bước tuỳ chọn nhưng nên làm: đăng ký webhook với GHTK để trạng thái tự về. Plugin hiện sẵn đường dẫn webhook trong tab cài đặt, anh em chỉ việc chép rồi gửi GHTK.
Plugin tự làm gì, phần nào vẫn bấm tay
- Plugin tự tính phí ship ở trang thanh toán (đường bộ; đường bay khi kho và khách khác tỉnh; hàng nặng BBS nếu bật), tự cập nhật trạng thái đơn qua webhook và tự gửi email mã vận đơn cho khách.
- Đăng đơn thì bấm một nút: hộp thoại mở ngay trong danh sách đơn hàng, anh em kiểm lại tiền thu hộ, ai trả phí, nhãn hàng rồi bấm Đăng ngay. Plugin không tự đẩy đơn mới sang GHTK và chưa có đăng đơn hàng loạt.
- Ngay trong đơn hàng in được nhãn PDF của GHTK để dán lên gói hàng, hoặc phiếu theo mẫu riêng có logo shop.
Với trang thanh toán dạng block (mặc định cho site mới từ WooCommerce 8.3), plugin chỉ chạy trên checkout block khi dùng địa chỉ 2 cấp. Shop vẫn dùng địa chỉ 3 cấp thì chuyển về trang thanh toán cổ điển, mình có bài chuyển trang checkout và giỏ hàng về giao diện cũ.
Muốn bấm thử trước khi quyết định thì có bản demo: sản phẩm đã nằm sẵn trong giỏ, anh em vào trang thanh toán chọn địa chỉ là thấy phí GHTK.
Nên tự code hay dùng plugin?
| Tự code bằng API | Dùng plugin | |
|---|---|---|
| Hợp với | Dev có thời gian; site có luồng đơn riêng như đồng bộ phần mềm kho, bán đa kênh | Chủ shop, dev/agency làm web cho khách cần chạy ngay |
| Thời gian | Tính bằng ngày hoặc tuần, tuỳ làm bao nhiêu phần | Khoảng 30 phút cài đặt |
| Ô địa chỉ tỉnh, phường/xã | Tự làm, tự cập nhật khi dữ liệu đổi | Có sẵn, cập nhật dữ liệu ngay trong trang cài đặt |
| Khi GHTK đổi API | Tự sửa | Cập nhật plugin |
| Tuỳ biến | Toàn quyền | Qua hook và filter có sẵn |
| Chi phí | Công lập trình và bảo trì | Phí license, xem ở trang sản phẩm |
Cách chọn của mình: shop bán hằng ngày, cần đủ năm phần thì dùng plugin, để dành thời gian cho việc bán hàng. Site có luồng đơn riêng (đẩy đơn từ phần mềm quản lý kho, gom đơn từ nhiều kênh) thì tự code để toàn quyền.
Còn nếu chỉ cần ô chọn địa chỉ Việt Nam ở trang thanh toán và tự đặt phí ship theo tỉnh, chưa cần nối GHTK, thì bản miễn phí Vietnam Checkout for WooCommerce trên WordPress.org (cũng của mình) là đủ. Cần tính phí tới từng phường/xã thì có bản Pro.
Lỗi hay gặp sau khi kết nối GHTK
- Trang thanh toán không hiện phí GHTK thì kiểm lần lượt: token đúng và còn hạn chưa (GHTK trả 403 khi token sai), có đang dùng token thật trong chế độ thử nghiệm không, địa chỉ kho đã đủ tỉnh và phường/xã chưa, sản phẩm đã có cân nặng chưa.
- Phí ship giống nhau ở mọi tỉnh là do khách đang nhận phí từ một phương thức Đồng giá trong khu vực giao hàng thay vì từ GHTK, tắt phương thức đó đi.
- Phí ship cao bất thường khi địa chỉ kho khai sai tỉnh, vì GHTK tính như gửi liên tỉnh.
- Đăng đơn báo địa chỉ không hợp lệ là do dữ liệu tỉnh, phường/xã của website lệch với GHTK (hay gặp sau sáp nhập), hoặc thiếu địa chỉ cấp 4 như thôn, ấp, tổ. Không biết địa chỉ cấp 4 thì điền “Khác”.
- Đơn giao xong mà trạng thái trên web không đổi có thể do webhook chưa được GHTK bật, website chạy ở máy local hoặc tên miền thử, Cloudflare hay tường lửa chặn request của GHTK, hoặc endpoint tự viết không trả về HTTP 200.
Dùng plugin thì xem thêm trang giải đáp thắc mắc về plugin GHTK, mục này được bổ sung sau mỗi ca hỗ trợ thực tế.
Câu hỏi thường gặp
Website không dùng WooCommerce có tích hợp GHTK được không?
Được. API của GHTK là các request HTTP thông thường, website nào gọi ra ngoài được là kết nối được qua đúng các endpoint ở trên, dù là WordPress không bán hàng, Laravel hay PHP thuần. Riêng plugin trong bài chỉ chạy với WooCommerce.
Có chạy thử mà không tạo vận đơn thật được không?
Có. Đăng ký tài khoản ở môi trường thử nghiệm khachhang-staging.ghtklab.com, lấy token ở đó rồi gọi tới services-staging.ghtklab.com. Với plugin thì bật Sandbox trong tab Cài đặt GHTK và dán token thử nghiệm vào. Tập xong nhớ trả lại token thật.
Địa chỉ 2 cấp sau sáp nhập ảnh hưởng gì tới việc tích hợp?
API tính phí của GHTK giờ chỉ bắt buộc tỉnh và phường/xã, quận/huyện không còn bắt buộc. Cái phải sửa nằm ở phía website: ô địa chỉ và bộ dữ liệu phải theo đơn vị hành chính mới, nếu không GHTK sẽ báo địa chỉ không hợp lệ lúc đăng đơn. Plugin GHTK có sẵn cả bộ 2 cấp lẫn 3 cấp, chọn trong phần cài đặt.
Token GHTK có hết hạn không?
Có. Lúc tạo token anh em chọn ngày hết hạn. Tới hạn thì mọi request bị từ chối (lỗi 403): phí ship không hiện, không đăng được đơn. Ghi ngày đó vào lịch và tạo token mới trước khi hết hạn.
Bắt đầu từ đâu
Tự code thì bắt đầu từ api.ghtk.vn và hai đoạn code ở trên. Muốn chạy ngay thì cài plugin, khoảng 30 phút là khách thấy phí ship thật ở trang thanh toán.
Anh em đang kết nối mà vướng chỗ nào thì để lại bình luận bên dưới, hoặc nhắn Zalo 0965.419.096, mình trả lời. Chúc anh em dán token xong là phí ship GHTK hiện đúng ngay lần thử đầu! 🚀




