Chương 12. Tích hợp dịch vụ bên ngoài

📚 Mục lục sách

Sản phẩm của bạn hiếm khi hoạt động một mình. Bạn sẽ cần cho khách đăng nhập bằng Google hoặc Facebook, tự động gửi email, nhận thanh toán qua cổng thanh toán, hoặc giao hàng và theo dõi vận đơn. EzyPlatform kết nối sẵn từng mảng việc này qua một plugin riêng — EzyLogin, EzyMail, EzyPayment, EzyDelivery — chương này giới thiệu từng plugin và nguyên tắc an toàn chung khi dùng chúng.

Đăng nhập bằng Google & Facebook với EzyLogin

Cài đặt thêm plugin EzyLogin (xem lại hướng dẫn cài đặt plugin cho EzyPlatform nếu cần) để cho phép khách đăng nhập bằng tài khoản Google hoặc Facebook thay vì phải đăng ký tài khoản mới — giúp khách vào thẳng trang mà không mất công điền form.

Toàn bộ quá trình diễn ra qua một vòng "chuyển hướng qua lại" giữa EzyPlatform và Google/Facebook, khách hàng không cần biết Client ID hay Redirect URI là gì — những thứ đó chỉ là phần bạn khai báo một lần lúc cấu hình:

flowchart LR
    A["Khách bấm nút<br/>Đăng nhập bằng Google/Facebook"] --> B["EzyPlatform chuyển hướng<br/>sang trang đăng nhập của Google/Facebook"]
    B --> C{"Khách đồng ý<br/>chia sẻ thông tin?"}
    C -- "Đồng ý" --> D["Google/Facebook gọi ngược về<br/>Redirect URI đã khai báo"]
    D --> E["EzyLogin xác thực,<br/>tạo hoặc cập nhật tài khoản"]
    E --> F["Khách đăng nhập thành công"]
    C -- "Từ chối" --> G["Quay lại trang đăng nhập,<br/>không tạo tài khoản mới"]

    classDef action fill:#3b82f6,stroke:#1d4ed8,color:#ffffff;
    classDef decision fill:#f59e0b,stroke:#b45309,color:#ffffff;
    classDef success fill:#10b981,stroke:#047857,color:#ffffff;
    classDef stop fill:#ef4444,stroke:#b91c1c,color:#ffffff;
    class A,B,D,E action;
    class C decision;
    class F success;
    class G stop;
    linkStyle default stroke:#94a3b8,stroke-width:1.5px;

Với mỗi nhà cung cấp, bạn đăng ký một ứng dụng trên Google Cloud Console hoặc Facebook for Developers để lấy Client ID (hay App ID) và Client Secret, khai vào trang cài đặt EzyLogin trên trang quản trị. Khi đăng ký ứng dụng, nền tảng sẽ hỏi bạn Redirect URI (địa chỉ họ gọi ngược về sau khi khách đăng nhập xong) — luôn khai đúng theo mẫu {tên miền của bạn}/ezylogin/callback/{tên nhà cung cấp}:

Nhà cung cấp Redirect URI cần khai báo Nơi lấy Client ID & Secret
Google /ezylogin/callback/google Google Cloud Console
Facebook /ezylogin/callback/facebook Facebook for Developers

Đây là hai nhà cung cấp EzyLogin hỗ trợ sẵn hiện nay. Sai một ký tự trong Redirect URI là nguyên nhân phổ biến nhất khiến nút "Đăng nhập bằng Google/Facebook" báo lỗi — nếu gặp lỗi này, việc đầu tiên nên kiểm tra là đối chiếu lại đúng địa chỉ đã khai trên Google/Facebook với địa chỉ website thật của bạn.

Gửi email tự động với EzyMail

EzyMail là plugin đứng sau mọi email tự động của EzyPlatform — kể cả các mẫu thư đơn hàng đã nhắc ở Chương 11. Chương trước nói về nội dung email (mẫu thư), còn phần này nói về đường truyền — email được gửi đi bằng cách nào. EzyMail hỗ trợ sáu phương thức gửi, bạn cấu hình tại trang cài đặt EzyMail trên trang quản trị và chỉ cần bật một phương thức đang thực sự dùng:

Phương thức Thông tin cần cấu hình
SMTP Host, cổng, tài khoản, mật khẩu, bật xác thực, bật STARTTLS, email & tên người gửi mặc định.
Mailgun Domain, API Key, endpoint, email & tên người gửi mặc định.
Amazon SES Access Key ID, Secret Access Key, vùng (region), endpoint, email & tên người gửi mặc định.
Mailchimp (Transactional) API Key, endpoint, email & tên người gửi mặc định.
Gmail API Client ID, Client Secret (ứng dụng OAuth trên Google Cloud Console) — bấm nút kết nối trên trang cài đặt để đăng nhập Google một lần, EzyMail tự lưu lại và tự làm mới token sau đó.
Microsoft Graph API Tenant ID, Client ID, Client Secret (ứng dụng đăng ký trên Microsoft Entra/Azure AD) — không cần đăng nhập tương tác, EzyMail tự lấy token bằng chính ứng dụng.

Bốn phương thức đầu (SMTP, Mailgun, Amazon SES, Mailchimp) chỉ cần một khóa API tĩnh là gửi được ngay; riêng Gmail API và Microsoft Graph API cần một bước lấy token trước khi gửi, mỗi bên theo một cách khác nhau:

flowchart TD
    A["Có email cần gửi<br/>(mẫu thư đơn hàng, marketing...)"] --> B["EzyMail đọc phương thức<br/>đang bật trong cài đặt"]
    B --> C{"Phương thức nào?"}
    C -- "SMTP / Mailgun /<br/>Amazon SES / Mailchimp" --> D["Gọi thẳng bằng<br/>khóa API đã khai"]
    C -- "Gmail API" --> E["Dùng Refresh Token<br/>(lấy một lần qua màn hình kết nối Google)"]
    E --> F["Tự làm mới Access Token<br/>khi sắp hết hạn"]
    C -- "Microsoft Graph API" --> G["Tự lấy Access Token mới<br/>bằng Client ID/Secret, không cần đăng nhập"]
    D --> H["Gửi email"]
    F --> H
    G --> H

    classDef start fill:#3b82f6,stroke:#1d4ed8,color:#ffffff;
    classDef decision fill:#f59e0b,stroke:#b45309,color:#ffffff;
    classDef oauth fill:#8b5cf6,stroke:#6d28d9,color:#ffffff;
    classDef success fill:#10b981,stroke:#047857,color:#ffffff;
    class A,B,D start;
    class C decision;
    class E,F,G oauth;
    class H success;
    linkStyle default stroke:#94a3b8,stroke-width:1.5px;

Với Gmail API, khi bấm nút kết nối, EzyMail chuyển bạn sang trang đăng nhập Google rồi gọi ngược về đúng theo mẫu Redirect URI của EzyLogin ở trên: {tên miền của bạn}/ezymail/settings/mail-delivery-services/gmail-api/oauth2/callback — bạn cần khai đúng địa chỉ này khi đăng ký ứng dụng OAuth trên Google Cloud Console, tương tự bước làm với EzyLogin.

📋 Prompt mẫu — Nhờ AI tư vấn chọn phương thức gửi email
Cửa hàng bánh của tôi hiện gửi khoảng 50 email đơn hàng mỗi ngày, chưa có sẵn tài khoản email doanh nghiệp. Hãy so sánh giúp tôi ưu nhược điểm của SMTP, Mailgun và Amazon SES trong trường hợp này, và gợi ý phương thức nào phù hợp nhất để tôi bắt đầu.

Nhận thanh toán trực tuyến với EzyPayment

Cài đặt plugin EzyPayment để nhận thanh toán online. Năm cổng được hỗ trợ chia làm hai nhóm, khác nhau ở chỗ khách hàng thanh toán ở đâu và EzyPlatform biết tiền đã về bằng cách nào:

Nhóm Cổng Trang thanh toán Cách EzyPlatform biết đã thanh toán
Chuyển hướng sang cổng VNPay Trang do VNPay quản lý (thẻ ATM, thẻ quốc tế, QR) VNPay gọi về /payment kèm chữ ký HMAC-SHA512
PayPal Trang do PayPal quản lý Xác nhận qua API PayPal khi khách hoàn tất
Chuyển khoản có xác nhận tự động PayFS /checkout/payfs (EzyPlatform tự hiển thị) Webhook PUT /api/v1/payfs/orders/{orderId}/bank-transferred kèm checksum
Pay2S /checkout/pay2s Webhook PUT /api/v1/pay2s/orders/{orderId}/bank-transferred kèm checksum
SePay /checkout/sepay Webhook PUT /api/v1/sepay/orders/{orderId}/bank-transferred kèm checksum

Với nhóm "chuyển hướng sang cổng" (VNPay, PayPal), khách rời khỏi website của bạn để thanh toán, nên bạn không bao giờ chạm vào thông tin thẻ của khách. Với nhóm "chuyển khoản" (PayFS, Pay2S, SePay), EzyPlatform tự hiển thị trang riêng với thông tin chuyển khoản/mã QR ngân hàng — khách chuyển khoản thủ công, rồi đối tác đứng sau các cổng này tự gọi webhook báo "đã nhận tiền" khi phát hiện giao dịch khớp.

flowchart TD
    A["Khách chọn cổng thanh toán<br/>lúc đặt hàng"] --> B{"Loại cổng?"}
    B -- "VNPay / PayPal" --> C["Chuyển hướng khách<br/>sang trang do cổng quản lý"]
    C --> D["Khách thanh toán trên trang của cổng"]
    D --> E["Cổng gọi về /payment<br/>kèm chữ ký xác thực"]
    B -- "PayFS / Pay2S / SePay" --> F["Hiển thị trang chuyển khoản/QR<br/>ngay trên website của bạn"]
    F --> G["Khách chuyển khoản thủ công"]
    G --> H["Đối tác gọi webhook<br/>.../bank-transferred kèm checksum"]
    E --> I{"Chữ ký/checksum hợp lệ?"}
    H --> I
    I -- "Hợp lệ" --> J["Cập nhật đơn hàng: Đã thanh toán"]
    I -- "Không hợp lệ" --> K["Từ chối, giữ nguyên trạng thái đơn"]

    classDef action fill:#3b82f6,stroke:#1d4ed8,color:#ffffff;
    classDef decision fill:#f59e0b,stroke:#b45309,color:#ffffff;
    classDef success fill:#10b981,stroke:#047857,color:#ffffff;
    classDef stop fill:#ef4444,stroke:#b91c1c,color:#ffffff;
    class A,C,D,E,F,G,H action;
    class B,I decision;
    class J success;
    class K stop;
    linkStyle default stroke:#94a3b8,stroke-width:1.5px;

Việc xác thực callback — tính lại chữ ký/checksum, so khớp, chấp nhận hay từ chối — đều do EzyPayment xử lý sẵn cho cả năm cổng; bạn chỉ cần khai đúng Client Key/Secret Key mà từng cổng cấp cho bạn vào trang cài đặt EzyPayment trên trang quản trị.

Nguyên tắc an toàn: không bao giờ tự lưu trữ thông tin thẻ ngân hàng của khách hàng trên hệ thống của bạn. EzyPayment không có bất kỳ chỗ nào để lưu số thẻ hay mã CVV — toàn bộ luồng thanh toán đều đưa khách sang trang do cổng thanh toán host, hoặc chỉ hiển thị thông tin chuyển khoản công khai (số tài khoản, nội dung chuyển khoản) chứ không phải dữ liệu thẻ; vai trò của EzyPlatform chỉ là khởi tạo yêu cầu và xác minh kết quả trả về.
📋 Prompt mẫu — Nhờ AI tư vấn chọn cổng thanh toán
Cửa hàng bánh của tôi chủ yếu bán cho khách trong nước, chưa có tài khoản doanh nghiệp ở cổng thanh toán nào. Hãy so sánh giúp tôi giữa việc dùng VNPay (khách quẹt thẻ/QR trên trang VNPay) và Pay2S/SePay (khách chuyển khoản, hệ thống tự đối soát) — chi phí, thời gian thiết lập, và trải nghiệm khách hàng bên nào tiện hơn để tôi bắt đầu.

Giao hàng và theo dõi vận đơn với EzyDelivery

Nếu bạn bán hàng vật lý (như cửa hàng bánh xuyên suốt cuốn sách này), cài thêm plugin EzyDelivery để tạo vận đơn và theo dõi trạng thái giao hàng tự động, thay vì gọi điện hỏi đơn vị vận chuyển từng đơn. Hai đơn vị được hỗ trợ đầy đủ hiện nay là Giao Hàng Tiết Kiệm (GHTK)Viettel Post:

Đơn vị vận chuyển Thông tin xác thực cần khai
Giao Hàng Tiết Kiệm (GHTK) Một mã Token duy nhất do GHTK cấp cho tài khoản đối tác của bạn.
Viettel Post Tài khoản (username/password) đã đăng ký với Viettel Post.

Bạn khai những thông tin này tại trang cài đặt EzyDelivery trên trang quản trị. Sau khi tạo vận đơn, đơn vị vận chuyển sẽ tự động gọi webhook về EzyPlatform mỗi khi trạng thái đơn thay đổi, EzyDelivery cập nhật lại trạng thái tương ứng mà bạn không cần thao tác thủ công:

flowchart LR
    A["Tạo vận đơn<br/>từ đơn hàng đã thanh toán"] --> B["Đã tiếp nhận<br/>(REQUEST_RECEIVED)"]
    B --> C["Đã lấy hàng<br/>(PICKED_UP)"]
    C --> D["Đang vận chuyển<br/>(IN_TRANSIT)"]
    D --> E["Giao thành công<br/>(DELIVERED)"]
    D -- "Giao không thành công" --> F["Không giao được<br/>(UNABLE_TO_DELIVER)"]
    F -- "Giao lại" --> D
    F -- "Không thể giao tiếp" --> G["Hoàn về người gửi<br/>(RETURNING_TO_SENDER)"]

    classDef progress fill:#3b82f6,stroke:#1d4ed8,color:#ffffff;
    classDef success fill:#10b981,stroke:#047857,color:#ffffff;
    classDef fail fill:#ef4444,stroke:#b91c1c,color:#ffffff;
    class A,B,C,D progress;
    class E success;
    class F,G fail;
    linkStyle default stroke:#94a3b8,stroke-width:1.5px;

Đây là một luồng rút gọn — mỗi đơn vị vận chuyển thực tế trả về nhiều trạng thái chi tiết hơn (ví dụ GHTK có cả trạng thái "đang đối soát tiền COD", "hoãn lấy hàng"...), EzyDelivery quy đổi tất cả về một bộ trạng thái chung để bạn không cần tự xử lý sự khác biệt giữa các đơn vị. Khách hàng cũng có thể tự tra cứu vận đơn của mình ngay trên website (trang tra cứu vận đơn công khai) mà không cần đăng nhập.

📋 Prompt mẫu — Nhờ AI giải thích trạng thái vận đơn cho khách
Hãy viết giúp tôi phần chú thích ngắn gọn, thân thiện cho từng trạng thái vận đơn trên trang tra cứu đơn hàng của cửa hàng bánh: đã tiếp nhận, đã lấy hàng, đang vận chuyển, giao thành công, không giao được, hoàn về người gửi — để khách không quen thuật ngữ logistics vẫn hiểu đơn của mình đang ở đâu.

Webhook: cầu nối thông báo giữa các hệ thống

Webhook là cách một hệ thống bên ngoài tự động gửi thông báo về EzyPlatform ngay khi có sự kiện xảy ra, thay vì để EzyPlatform phải liên tục hỏi lại "có gì mới chưa?". Cả EzyPayment (khi khách thanh toán xong) lẫn EzyDelivery (khi trạng thái vận đơn thay đổi) đều hoạt động theo cơ chế này. Dù mỗi plugin gọi tên khác nhau, bản chất luồng xử lý bên dưới đều giống hệt nhau:

flowchart LR
    A["Sự kiện xảy ra ở hệ thống ngoài<br/>(khách thanh toán, vận đơn đổi trạng thái...)"] --> B["Hệ thống ngoài gọi<br/>đến URL webhook đã đăng ký sẵn"]
    B --> C["EzyPlatform nhận request<br/>tại endpoint tương ứng"]
    C --> D{"Chữ ký/checksum<br/>hợp lệ?"}
    D -- "Hợp lệ" --> E["Cập nhật dữ liệu tương ứng<br/>(đơn hàng, vận đơn...)"]
    D -- "Không hợp lệ" --> F["Từ chối request,<br/>giữ nguyên dữ liệu cũ"]

    classDef action fill:#3b82f6,stroke:#1d4ed8,color:#ffffff;
    classDef decision fill:#f59e0b,stroke:#b45309,color:#ffffff;
    classDef success fill:#10b981,stroke:#047857,color:#ffffff;
    classDef fail fill:#ef4444,stroke:#b91c1c,color:#ffffff;
    class A,B,C action;
    class D decision;
    class E success;
    class F fail;
    linkStyle default stroke:#94a3b8,stroke-width:1.5px;

Vì địa chỉ webhook là một URL công khai, ai cũng có thể gửi request giả tới đó để đánh lừa hệ thống — ví dụ giả vờ báo "đã thanh toán" cho một đơn hàng chưa hề được trả tiền. Đây là lý do mọi webhook trong EzyPayment và EzyDelivery đều đi kèm một bước xác minh chữ ký hoặc mã checksum, tính từ khóa bí mật chỉ bạn và đối tác biết, trước khi tin bất kỳ dữ liệu nào từ request gọi đến. Riêng webhook của EzyDelivery còn có thêm một lớp kiểm tra nữa: sau khi khóa bí mật hợp lệ, hệ thống đối chiếu tiếp mã vận đơn (tracking ID) trong request với mã đã lưu cho đúng đơn hàng đó — để một request hợp lệ về mặt khóa bí mật cũng không thể vô tình (hay cố ý) cập nhật nhầm sang đơn hàng khác. Bạn không cần tự viết logic xác thực này — chỉ cần khai đúng khóa bí mật ở bước cài đặt, phần còn lại EzyPlatform đã lo.

Plugin Sự kiện Endpoint nhận webhook Cách xác minh
EzyPayment — VNPay Khách thanh toán hoàn tất GET /payment Chữ ký HMAC-SHA512
EzyPayment — PayFS / Pay2S / SePay Đối tác xác nhận đã nhận chuyển khoản PUT /api/v1/{tên cổng}/orders/{orderId}/bank-transferred Checksum
EzyDelivery — GHTK / Viettel Post Trạng thái vận đơn thay đổi POST /delivery/callback/{mã đơn vị vận chuyển} Khóa bí mật riêng từng đơn vị + đối chiếu mã vận đơn

Những địa chỉ này chính là "Redirect URI" của EzyLogin nhìn từ một góc khác: cả hai đều là một URL bạn đăng ký sẵn để hệ thống bên ngoài gọi ngược về, chỉ khác ở chỗ Redirect URI phục vụ việc đăng nhập còn webhook phục vụ việc thông báo sự kiện. Khi tích hợp thêm một dịch vụ mới ngoài danh sách này, hãy tìm đúng endpoint tương ứng trong tài liệu của EzyPlatform và luôn kiểm tra bước xác minh chữ ký/checksum đã được bật, trước khi đưa vào vận hành thật.

📋 Prompt mẫu — Nhờ AI giải thích luồng xác minh webhook
Hãy giải thích ngắn gọn, dễ hiểu cho một người không chuyên kỹ thuật: tại sao webhook xác nhận thanh toán cần chữ ký/checksum, và điều gì sẽ xảy ra nếu bỏ qua bước xác minh này.
Plugin Dùng để làm gì Nhà cung cấp hỗ trợ đầy đủ
EzyLogin Cho khách đăng nhập bằng tài khoản mạng xã hội có sẵn Google, Facebook
EzyMail Gửi email tự động (đường truyền, không phải nội dung) SMTP, Mailgun, Amazon SES, Mailchimp, Gmail API, Microsoft Graph API
EzyPayment Nhận thanh toán trực tuyến an toàn VNPay, PayPal (và PayFS, Pay2S, SePay dạng chuyển khoản)
EzyDelivery Tạo vận đơn, theo dõi và cập nhật trạng thái giao hàng tự động GHTK, Viettel Post

📌 Tóm tắt chương

  • EzyLogin cho khách đăng nhập bằng Google/Facebook — cần Client ID/Secret và khai đúng Redirect URI dạng /ezylogin/callback/{tên nhà cung cấp}.
  • EzyMail lo phần đường truyền gửi email — SMTP, Mailgun, Amazon SES, Mailchimp, Gmail API hoặc Microsoft Graph API — tách biệt với nội dung mẫu thư đã học ở Chương 11.
  • EzyPayment xử lý thanh toán qua VNPay, PayPal và các cổng chuyển khoản — không bao giờ tự lưu thông tin thẻ ngân hàng của khách.
  • EzyDelivery tạo vận đơn và tự cập nhật trạng thái giao hàng qua GHTK, Viettel Post.
  • Webhook là cơ chế chung giúp các dịch vụ bên ngoài tự thông báo cho EzyPlatform; luôn có bước xác minh chữ ký/checksum để chặn request giả mạo.
Chương trước: để hệ thống tự vận hành thay vì làm thủ công lặp đi lặp lại. ← Đọc Chương 11
Chương tiếp theo: tự phát triển module/plugin riêng bằng AI để mở rộng EzyPlatform. Đọc Chương 13 →