Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.lux-core.io/llms.txt

Use this file to discover all available pages before exploring further.

Go Examples

Complete Go examples for integrating with the LuxCore API using the standard library.

LuxCore Client Package

package luxcore

import (
	"bytes"
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"math"
	"net/http"
	"net/url"
	"os"
	"time"
)

const BaseURL = "https://api.lux-core.io/api/v1"

type Client struct {
	APIKey     string
	HTTPClient *http.Client
	BaseURL    string
}

type LuxCoreError struct {
	Message    string
	StatusCode int
	ErrorCode  string
}

func (e *LuxCoreError) Error() string {
	return fmt.Sprintf("LuxCore API error (%d): %s", e.StatusCode, e.Message)
}

func NewClient(apiKey string) *Client {
	if apiKey == "" {
		apiKey = os.Getenv("LUXCORE_API_KEY")
	}
	return &Client{
		APIKey:     apiKey,
		HTTPClient: &http.Client{Timeout: 30 * time.Second},
		BaseURL:    BaseURL,
	}
}

func (c *Client) request(method, path string, body interface{}, params url.Values) ([]byte, error) {
	reqURL := c.BaseURL + path
	if params != nil {
		reqURL += "?" + params.Encode()
	}

	var reqBody io.Reader
	if body != nil {
		jsonBody, err := json.Marshal(body)
		if err != nil {
			return nil, err
		}
		reqBody = bytes.NewBuffer(jsonBody)
	}

	req, err := http.NewRequest(method, reqURL, reqBody)
	if err != nil {
		return nil, err
	}

	req.Header.Set("X-API-Key", c.APIKey)
	req.Header.Set("Content-Type", "application/json")

	resp, err := c.HTTPClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	respBody, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	if resp.StatusCode >= 400 {
		var errResp struct {
			Message string `json:"message"`
			Error   string `json:"error"`
		}
		json.Unmarshal(respBody, &errResp)
		return nil, &LuxCoreError{
			Message:    errResp.Message,
			StatusCode: resp.StatusCode,
			ErrorCode:  errResp.Error,
		}
	}

	return respBody, nil
}

// Payment types
type PaymentRequest struct {
	Amount            int64                  `json:"amount"`
	Currency          string                 `json:"currency"`
	Method            string                 `json:"method"`
	Type              string                 `json:"type"`
	MerchantReference string                 `json:"merchant_reference"`
	Description       string                 `json:"description,omitempty"`
	Customer          Customer               `json:"customer"`
	Payout            *PayoutDetails         `json:"payout,omitempty"`
	Metadata          map[string]interface{} `json:"metadata,omitempty"`
}

type Customer struct {
	Name       string `json:"name"`
	Email      string `json:"email"`
	Phone      string `json:"phone,omitempty"`
	ExternalID string `json:"external_id,omitempty"`
}

type PayoutDetails struct {
	RecipientName    string `json:"recipient_name"`
	BankAccount      string `json:"bank_account"`
	BankCode         string `json:"bank_code"`
	Reference        string `json:"reference,omitempty"`
	AccountType      string `json:"account_type,omitempty"`
	BeneficiaryTaxID string `json:"beneficiary_tax_id,omitempty"`
	Description      string `json:"description,omitempty"`
	PayID     string `json:"payid,omitempty"`
	PayIDType string `json:"payid_type,omitempty"`
}

type Payment struct {
	TransactionID     string                 `json:"transaction_id"`
	Status            string                 `json:"status"`
	Amount            int64                  `json:"amount"`
	Currency          string                 `json:"currency"`
	Method            *string                `json:"method"`
	Type              string                 `json:"type"`
	MerchantReference string                 `json:"merchant_reference"`
	FeeAmount         *int64                 `json:"fee_amount,omitempty"`
	NetAmount         *int64                 `json:"net_amount,omitempty"`
	ErrorCode         string                 `json:"error_code,omitempty"`
	ErrorMessage      string                 `json:"error_message,omitempty"`
	CreatedAt         time.Time              `json:"created_at"`
	BankDetails       *BankDetails           `json:"bank_details,omitempty"`
	Metadata          map[string]interface{} `json:"metadata,omitempty"`
}

type BankDetails struct {
	Amount        string  `json:"amount"`
	Purpose       string  `json:"purpose"`
	Currency      string  `json:"currency"`
	BankName      string  `json:"bank_name"`
	SwiftCode     *string `json:"swift_code"`
	AccountHolder string  `json:"account_holder"`
	AccountNumber string  `json:"account_number"`
}

type PaymentListResponse struct {
	Success    bool       `json:"success"`
	Data       []Payment  `json:"data"`
	Total      int        `json:"total"`
	Pagination *Pagination `json:"pagination,omitempty"`
}

type Pagination struct {
	Limit      int `json:"limit"`
	Offset     int `json:"offset"`
	TotalPages int `json:"total_pages"`
}

// Payment methods
func (c *Client) CreatePayment(req PaymentRequest) (*Payment, error) {
	respBody, err := c.request("POST", "/payments", req, nil)
	if err != nil {
		return nil, err
	}

	var payment Payment
	if err := json.Unmarshal(respBody, &payment); err != nil {
		return nil, err
	}

	return &payment, nil
}

func (c *Client) GetPayment(paymentID string) (*Payment, error) {
	respBody, err := c.request("GET", "/payments/"+paymentID, nil, nil)
	if err != nil {
		return nil, err
	}

	var payment Payment
	if err := json.Unmarshal(respBody, &payment); err != nil {
		return nil, err
	}

	return &payment, nil
}

func (c *Client) ListPayments(filters map[string]string) (*PaymentListResponse, error) {
	params := url.Values{}
	for k, v := range filters {
		params.Set(k, v)
	}

	respBody, err := c.request("GET", "/payments", nil, params)
	if err != nil {
		return nil, err
	}

	var result PaymentListResponse
	if err := json.Unmarshal(respBody, &result); err != nil {
		return nil, err
	}

	return &result, nil
}

func (c *Client) CancelPayment(paymentID string) error {
	_, err := c.request("POST", "/payments/"+paymentID+"/cancel", nil, nil)
	return err
}

// Balance
type BalanceResponse struct {
	Success          bool   `json:"success"`
	MerchantID       int    `json:"merchant_id"`
	Currency         string `json:"currency"`
	BalanceType      string `json:"balance_type"`
	BalanceAmount    string `json:"balance_amount"`
	AvailableAmount  string `json:"available_amount"`
	FrozenAmount     string `json:"frozen_amount"`
	NewBalance       int    `json:"new_balance"`
	AvailableBalance int    `json:"available_balance"`
}

func (c *Client) GetBalance(currency string) (*BalanceResponse, error) {
	params := url.Values{}
	if currency != "" {
		params.Set("currency", currency)
	}

	respBody, err := c.request("GET", "/balance", nil, params)
	if err != nil {
		return nil, err
	}

	var balance BalanceResponse
	if err := json.Unmarshal(respBody, &balance); err != nil {
		return nil, err
	}

	return &balance, nil
}

// Webhook verification
func VerifyWebhookSignature(payload []byte, signature string, timestamp int64, secret string) error {
	// Check timestamp (reject if older than 5 minutes)
	currentTime := time.Now().Unix()
	if math.Abs(float64(currentTime-timestamp)) > 300 {
		return fmt.Errorf("webhook timestamp too old")
	}

	// Calculate expected signature
	payloadToSign := fmt.Sprintf("%d.%s", timestamp, string(payload))
	mac := hmac.New(sha256.New, []byte(secret))
	mac.Write([]byte(payloadToSign))
	expectedSignature := "hmac_sha256=" + hex.EncodeToString(mac.Sum(nil))

	// Compare signatures
	if !hmac.Equal([]byte(signature), []byte(expectedSignature)) {
		return fmt.Errorf("invalid webhook signature")
	}

	return nil
}

Usage Examples

Create a Deposit Payment

package main

import (
	"fmt"
	"log"
	"time"

	"your-project/luxcore"
)

func main() {
	client := luxcore.NewClient("")

	payment, err := client.CreatePayment(luxcore.PaymentRequest{
		Amount:            100000, // $1,000.00 MXN (in centavos)
		Currency:          "MXN",
		Method:            "spei",
		Type:              "deposit",
		MerchantReference: fmt.Sprintf("order_%d", time.Now().Unix()),
		Customer: luxcore.Customer{
			Name:  "Juan Perez",
			Email: "juan@example.com",
			Phone: "+525551234567",
		},
		Metadata: map[string]interface{}{
			"order_id": "12345",
			"source":   "web",
		},
	})
	if err != nil {
		log.Fatalf("Failed to create payment: %v", err)
	}

	fmt.Printf("Payment created: %s\n", payment.TransactionID)
	fmt.Printf("Status: %s\n", payment.Status)

	if payment.BankDetails != nil {
		fmt.Printf("Bank: %s\n", payment.BankDetails.BankName)
		fmt.Printf("Account: %s\n", payment.BankDetails.AccountNumber)
		fmt.Printf("Holder: %s\n", payment.BankDetails.AccountHolder)
	}
}

Create a Withdrawal (Payout)

payout, err := client.CreatePayment(luxcore.PaymentRequest{
	Amount:            50000,
	Currency:          "MXN",
	Method:            "spei",
	Type:              "withdrawal",
	MerchantReference: fmt.Sprintf("payout_%d", time.Now().Unix()),
	Customer: luxcore.Customer{
		Name:  "Finance Team",
		Email: "finance@company.com",
	},
	Payout: &luxcore.PayoutDetails{
		RecipientName: "Maria Garcia",
		BankAccount:   "012345678901234567",
		BankCode:      "002",
		Reference:     "PAYOUT-001",
	},
})
if err != nil {
	log.Fatal(err)
}

fmt.Printf("Payout created: %s\n", payout.TransactionID)

Webhook Handler (net/http)

package main

import (
	"encoding/json"
	"io"
	"log"
	"net/http"
	"os"
	"strconv"

	"your-project/luxcore"
)

var webhookSecret = os.Getenv("LUXCORE_WEBHOOK_SECRET")

// WebhookPayload uses the envelope structure
type WebhookPayload struct {
	ID         string      `json:"id"`
	CreatedAt  string      `json:"created_at"`
	MerchantID int         `json:"merchant_id"`
	Data       WebhookData `json:"data"`
}

type WebhookData struct {
	Payment json.RawMessage `json:"payment"`
}

func webhookHandler(w http.ResponseWriter, r *http.Request) {
	// Read body
	body, err := io.ReadAll(r.Body)
	if err != nil {
		http.Error(w, "Failed to read body", http.StatusBadRequest)
		return
	}

	// Get headers
	signature := r.Header.Get("X-Webhook-Signature")
	timestampStr := r.Header.Get("X-Webhook-Timestamp")
	timestamp, _ := strconv.ParseInt(timestampStr, 10, 64)
	event := r.Header.Get("X-Webhook-Event")
	webhookId := r.Header.Get("X-Webhook-Id")

	// Verify signature
	if err := luxcore.VerifyWebhookSignature(body, signature, timestamp, webhookSecret); err != nil {
		log.Printf("Webhook verification failed: %v", err)
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	// Parse payload (envelope structure)
	var payload WebhookPayload
	if err := json.Unmarshal(body, &payload); err != nil {
		http.Error(w, "Invalid JSON", http.StatusBadRequest)
		return
	}

	// Process event from header
	log.Printf("Received webhook event: %s (id: %s)", event, webhookId)

	switch event {
	case "payment.completed":
		log.Println("Payment completed!")
		// Handle completed payment
	case "payment.failed":
		log.Println("Payment failed!")
		// Handle failed payment
	case "payment.cancelled":
		log.Println("Payment cancelled!")
		// Handle cancelled payment
	default:
		log.Printf("Unhandled event: %s", event)
	}

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
	json.NewEncoder(w).Encode(map[string]bool{"received": true})
}

func main() {
	http.HandleFunc("/webhooks/luxcore", webhookHandler)
	log.Println("Starting webhook server on :3000")
	log.Fatal(http.ListenAndServe(":3000", nil))
}

Error Handling

payment, err := client.CreatePayment(req)
if err != nil {
	if luxErr, ok := err.(*luxcore.LuxCoreError); ok {
		switch luxErr.StatusCode {
		case 401:
			log.Println("Invalid API key")
		case 400:
			log.Printf("Validation error: %s", luxErr.Message)
		case 429:
			log.Println("Rate limited - waiting to retry")
			time.Sleep(60 * time.Second)
			// Retry...
		case 500, 503:
			log.Println("Server error - retrying")
			time.Sleep(5 * time.Second)
			// Retry...
		default:
			log.Printf("API error (%d): %s", luxErr.StatusCode, luxErr.Message)
		}
	} else {
		log.Printf("Network error: %v", err)
	}
	return
}

Complete Example with Gin

package main

import (
	"log"
	"net/http"
	"os"

	"github.com/gin-gonic/gin"
	"your-project/luxcore"
)

var client *luxcore.Client

func main() {
	client = luxcore.NewClient(os.Getenv("LUXCORE_API_KEY"))

	r := gin.Default()

	// Create payment endpoint
	r.POST("/api/payments", createPaymentHandler)

	// Webhook endpoint
	r.POST("/webhooks/luxcore", webhookHandler)

	r.Run(":8080")
}

func createPaymentHandler(c *gin.Context) {
	var req struct {
		Amount   int64  `json:"amount" binding:"required"`
		OrderID  string `json:"order_id" binding:"required"`
		Customer struct {
			Name  string `json:"name" binding:"required"`
			Email string `json:"email" binding:"required"`
		} `json:"customer" binding:"required"`
	}

	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	payment, err := client.CreatePayment(luxcore.PaymentRequest{
		Amount:            req.Amount,
		Currency:          "MXN",
		Method:            "spei",
		Type:              "deposit",
		MerchantReference: req.OrderID,
		Customer: luxcore.Customer{
			Name:  req.Customer.Name,
			Email: req.Customer.Email,
		},
	})
	if err != nil {
		if luxErr, ok := err.(*luxcore.LuxCoreError); ok {
			c.JSON(luxErr.StatusCode, gin.H{"error": luxErr.Message})
			return
		}
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Payment creation failed"})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"payment_id":   payment.TransactionID,
		"status":       payment.Status,
		"bank_details": payment.BankDetails,
	})
}