The payOS Go library provides convenient access to the payOS Merchant API from applications written in Go.
To learn how to use payOS Merchant API, checkout our API Reference and Documentation. We also have some examples in Examples.
Go 1.21 or higher.
go get github.com/payOSHQ/payos-lib-golang/v2Important
If update from v1, check Migration guide for detail migration.
First you need initialize the client to interacting with payOS Merchant API.
import (
"github.com/payOSHQ/payos-lib-golang/v2"
)
client, err := payos.NewPayOS(&payos.PayOSOptions{
ClientId: "your-client-id",
ApiKey: "your-api-key",
ChecksumKey: "your-checksum-key",
// ... other options
})
if err != nil {
log.Fatal(err)
}Then you can interact with payOS Merchant API, example create a payment link using PaymentRequests.Create().
paymentLink, err := client.PaymentRequests.Create(context.Background(), payos.CreatePaymentLinkRequest{
OrderCode: 123,
Amount: 2000,
Description: "payment",
ReturnUrl: "https://your-url.com",
CancelUrl: "https://your-url.com",
})
if err != nil {
log.Fatal(err)
}You can register an endpoint to receive the payment webhook.
confirmResult, err := client.Webhooks.Confirm(context.Background(), "https://your-url.com/payos-webhook")
if err != nil {
log.Fatal(err)
}Then using Webhooks.VerifyData() to verify and receive webhook data.
webhookBody := map[string]interface{}{
"code": "00",
"desc": "success",
"success": true,
"data": map[string]interface{}{
"orderCode": 123,
"amount": 3000,
"description": "VQRIO123",
"accountNumber": "12345678",
"reference": "TF230204212323",
"transactionDateTime": "2023-02-04 18:25:00",
"currency": "VND",
"paymentLinkId": "124c33293c43417ab7879e14c8d9eb18",
"code": "00",
"desc": "Thành công",
"counterAccountBankId": "",
"counterAccountBankName": "",
"counterAccountName": "",
"counterAccountNumber": "",
"virtualAccountName": "",
"virtualAccountNumber": "",
},
"signature": "8d8640d802576397a1ce45ebda7f835055768ac7ad2e0bfb77f9b8f12cca4c7f",
}
webhookData, err := client.Webhooks.VerifyData(webhookBody, client.ChecksumKey)
if err != nil {
log.Fatal(err)
}For more information about webhooks, see the API doc.
When the API return a non-success status code (i.e, 4xx or 5xx response) or non-success code data (any code except '00'), an error will be returned:
_, err := client.PaymentRequests.Get(context.Background(), "not-found-order-code")
if err != nil {
var apiErr *apierror.APIError
if errors.As(err, &apiErr) {
fmt.Println(apiErr.StatusCode)
fmt.Println(apiErr.Code)
fmt.Println(apiErr.Message)
fmt.Println(apiErr.Headers)
} else {
log.Fatal(err)
}
}List method in the payOS Merchant API are paginated. You can use the iterator to automatically fetch all pages:
// Auto pagination
limit := 3
iterator := client.Payouts.ListAutoPaging(context.Background(), &payos.GetPayoutListParams{
Limit: &limit,
})
for iterator.Next() {
payout := iterator.Current()
fmt.Printf("Payout ID: %s\n", payout.Id)
}
if err := iterator.Err(); err != nil {
log.Fatal(err)
}Or you can request single page at a time:
// Manual pagination
limit := 3
page, err := client.Payouts.List(context.Background(), &payos.GetPayoutListParams{
Limit: &limit,
})
if err != nil {
log.Fatal(err)
}
for _, payout := range page.Data {
fmt.Printf("Payout ID: %s\n", payout.Id)
}
for page.HasNextPage() {
page, err = page.GetNextPage()
if err != nil {
log.Fatal(err)
}
for _, payout := range page.Data {
fmt.Printf("Payout ID: %s\n", payout.Id)
}
}You can customize the payOS client with various options:
client, err := payos.NewPayOS(&payos.PayOSOptions{
ClientId: "your-client-id",
ApiKey: "your-api-key",
ChecksumKey: "your-checksum-key",
PartnerCode: "your-partner-code", // Optional partner code
BaseURL: "https://api-merchant.payos.vn", // Custom base URL
Timeout: 30 * time.Second, // Request timeout (default: 60s)
MaxRetries: 3, // Maximum retry attempts (default: 2)
HTTPClient: &http.Client{}, // Custom HTTP client
DebugLogger: log.New(os.Stderr, "[payOS] ", log.LstdFlags), // Enable debug logging
})You can use context for request cancellation and timeout:
// With timeout
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
paymentLink, err := client.PaymentRequests.Create(ctx, payos.CreatePaymentLinkRequest{
OrderCode: 123,
Amount: 2000,
Description: "payment",
ReturnUrl: "https://your-url.com",
CancelUrl: "https://your-url.com",
})
// With cancellation
ctx, cancel := context.WithCancel(context.Background())
go func() {
time.Sleep(5 * time.Second)
cancel() // Cancel after 5 seconds
}()
paymentLink, err := client.PaymentRequests.Create(ctx, paymentData)You can add custom middleware to intercept and modify HTTP requests:
// Custom logging middleware
loggingMiddleware := func(next payos.RequestHandler) payos.RequestHandler {
return func(ctx context.Context, req *http.Request) (*http.Response, error) {
start := time.Now()
log.Printf("Request: %s %s", req.Method, req.URL.Path)
resp, err := next(ctx, req)
duration := time.Since(start)
if err != nil {
log.Printf("Request failed after %v: %v", duration, err)
} else {
log.Printf("Request completed in %v: %d", duration, resp.StatusCode)
}
return resp, err
}
}
client, err := payos.NewPayOS(&payos.PayOSOptions{
ClientId: "your-client-id",
ApiKey: "your-api-key",
ChecksumKey: "your-checksum-key",
Middlewares: []payos.Middleware{loggingMiddleware},
})Enable debug logging to see detailed request and response information:
import (
"log"
"os"
)
client, err := payos.NewPayOS(&payos.PayOSOptions{
ClientId: "your-client-id",
ApiKey: "your-api-key",
ChecksumKey: "your-checksum-key",
DebugLogger: log.New(os.Stderr, "[payOS Debug] ", log.LstdFlags),
})For advanced use cases, you can make direct API calls with signature options:
// GET request with response signature verification
result, err := client.Client.Get(
context.Background(),
"/v2/payment-requests",
nil, // query parameters
nil, // headers
&payos.SignatureOpts{Response: "body"}, // verify response signature from body
)
// POST request with request and response signatures
result, err := client.Client.Post(
context.Background(),
"/v2/payment-requests",
requestData,
&payos.SignatureOpts{
Request: "create-payment-link", // sign request
Response: "body", // verify response signature from body
},
nil, // headers
)
// POST request with header signature
result, err := client.Client.Post(
context.Background(),
"/v1/payouts",
payoutData,
&payos.SignatureOpts{
Request: "header", // sign request in header
Response: "body", // verify response signature from body
},
map[string]string{"x-idempotency-key": "unique-key"},
)
// With custom options using RequestOptions
result, err := client.Client.Request(context.Background(), &payos.RequestOptions{
Method: "POST",
Path: "/v2/payment-requests",
Body: requestData,
SignatureOpts: &payos.SignatureOpts{
Request: "create-payment-link",
Response: "body",
},
})The SignatureOpts struct has two fields:
Request: Signature type for request - can be"create-payment-link","body", or"header"Response: Signature type for response verification - can be"body"or"header"
The signature can be manually created using crypto functions:
import (
"github.com/payOSHQ/payos-lib-golang/v2/internal/crypto"
)
// For create-payment-link signature
signature, err := crypto.CreateSignatureOfPaymentRequest(data, checksumKey)
// For payment-requests and webhook signature
signature, err := crypto.CreateSignatureFromObj(data, checksumKey)
// For payouts signature (used in headers)
signature, err := crypto.CreateSignature(checksumKey, data, nil)