Introduction
Relevant source files
The following files were used as context for generating this wiki page:
The zalopay repository contains a Go-based web service designed to integrate with the ZaloPay payment gateway. It provides a minimal API for creating payment orders, querying their status, and handling callbacks from ZaloPay. The service is built using the Gin web framework, demonstrating a basic structure for handling payment-related HTTP requests and interacting with the ZaloPay API.
The project is structured with a clear separation of concerns, dividing logic into controllers for handling HTTP requests and services for implementing business logic and communication with external APIs. Key functionalities include generating the required HMAC signatures for API authentication, creating new payment transactions, and processing asynchronous notifications from ZaloPay.
Sources: main.go, controller/payment_controller.go, services/payment.go
System Architecture
The application follows a layered architecture common in web services. The entry point (main.go) initializes a Gin router and maps API endpoints to specific handler functions within the PaymentController. The controller then delegates the business logic to the OrderService and PaymentService, which are responsible for internal order management and communication with the ZaloPay external API, respectively.
Sources: main.go:12-24, controller/payment_controller.go
The following diagram illustrates the high-level component interaction:
Core Functionalities
The service exposes three primary functionalities through its API: creating a payment order, handling a payment callback, and querying an order's status.
Order Creation
To initiate a payment, a client sends a request to the /api/v1/order/create endpoint. The PaymentController handles this request by first creating an internal order representation to generate a unique app_trans_id. It then calls the PaymentService to construct and send a request to the ZaloPay CreateOrderURL. This process involves generating an HMAC signature to authenticate the request. If successful, ZaloPay returns a payment URL (order_url), which is then sent back to the client.
The sequence diagram below shows the order creation flow.
Payment Callback Handling
After a user completes a payment, ZaloPay sends an asynchronous POST request to the /api/v1/payment/callback endpoint configured in the merchant's ZaloPay dashboard. The Callback function in the PaymentController is responsible for processing this notification.
The primary task of the callback handler is to verify the integrity and authenticity of the incoming data. It does this by recalculating the HMAC signature based on the received data and comparing it with the mac field in the callback payload. If the signatures match, the application can safely trust the callback and proceed with updating the internal order status (though this final step is not fully implemented in the provided source).
The validation logic is depicted in the flowchart below:
Order Status Query
The service provides an endpoint at /api/v1/order/query to check the status of a previously created transaction. The QueryOrder function in the controller takes an app_trans_id from the request query parameters. It then calls the PaymentService, which constructs a request for the ZaloPay QueryOrderURL. This request also requires an HMAC signature for authentication, generated using a specific data format. ZaloPay's response indicates the current status of the transaction (e.g., success, pending, failed).
Sources: controller/payment_controller.go:62-73, services/payment.go:48-73
ZaloPay Integration Details
HMAC Signature Generation
All authenticated requests to the ZaloPay API require a mac field, which is an HMAC-SHA256 signature of the request data. The common.HmacSHA256 function implements this logic. The string to be signed is a concatenation of specific request parameters separated by a | character.
For creating an order, the signature string is:
appid|apptransid|appuser|amount|apptime|embeddata|item
The PaymentService uses the configured Key1 for signing order creation and query requests, while the PaymentController uses Key2 for validating incoming callbacks.
// common/hmac.go:9-14
func HmacSHA256(s string, key string) string {
mac := hmac.New(sha256.New, []byte(key))
mac.Write([]byte(s))
return hex.EncodeToString(mac.Sum(nil))
}
Sources: common/hmac.go:9-14, services/payment.go:30, controller/payment_controller.go:53
API Endpoints
The service exposes a simple RESTful API for payment operations. All endpoints are grouped under the /api/v1 path.
| Method | Path | Controller Function | Description |
|---|---|---|---|
POST | /order/create | CreateOrder | Creates a new ZaloPay payment order. |
POST | /payment/callback | Callback | Handles the payment notification from ZaloPay. |
GET | /order/query | QueryOrder | Queries the status of an existing transaction. |
Sources: main.go:17-23, controller/payment_controller.go
Conclusion
The zalopay project serves as a clear, albeit minimal, example of a backend service for integrating with the ZaloPay payment gateway. It correctly implements the fundamental requirements of the ZaloPay API, including order creation, status querying, and secure callback handling through HMAC signature validation. Its modular structure, separating routing, request handling, and service logic, provides a solid foundation for building a more robust payment processing system.