ข้ามไปยังเนื้อหาหลัก

Go Library (omise-go)

ไลบรารี omise-go มีอินเทอร์เฟซ Go ที่เป็นธรรมชาติสำหรับ Omise API พร้อมด้วยการสนับสนุน goroutines, channels, การดำเนินการที่รับรู้ context และแนวทางปฏิบัติที่ทันสมัยของ Go

การติดตั้ง

การใช้ go get

go get github.com/omise/omise-go/v2

การใช้ go.mod

require github.com/omise/omise-go/v2 v2.0.0

ข้อกำหนด

  • Go 1.16 ขึ้นไป (รวมถึง Go 1.20+)
  • Go modules สำหรับการจัดการ dependency

เริ่มต้นอย่างรวดเร็ว

การกำหนดค่าพื้นฐาน

package main

import (
"github.com/omise/omise-go/v2"
"github.com/omise/omise-go/v2/operations"
)

func main() {
client, err := omise.NewClient(
"pkey_test_123456789",
"skey_test_123456789",
)
if err != nil {
panic(err)
}
}

ด้วยตัวแปรสภาพแวดล้อม

import (
"os"
"github.com/omise/omise-go/v2"
)

func initClient() (*omise.Client, error) {
return omise.NewClient(
os.Getenv("OMISE_PUBLIC_KEY"),
os.Getenv("OMISE_SECRET_KEY"),
)
}

ด้วยการกำหนดค่า Struct

type Config struct {
OmisePublicKey string
OmiseSecretKey string
APIVersion string
}

func NewOmiseClient(config Config) (*omise.Client, error) {
client, err := omise.NewClient(
config.OmisePublicKey,
config.OmiseSecretKey,
)
if err != nil {
return nil, err
}

client.SetAPIVersion(config.APIVersion)
return client, nil
}

ตัวแปรสภาพแวดล้อม

# การพัฒนา/ทดสอบ
export OMISE_SECRET_KEY=skey_test_123456789
export OMISE_PUBLIC_KEY=pkey_test_123456789

# ผลิตภาพ
# export OMISE_SECRET_KEY=skey_live_123456789
# export OMISE_PUBLIC_KEY=pkey_live_123456789

การดำเนินการทั่วไป

การสร้างค่าธรรมเนียม

package main

import (
"context"
"fmt"
"github.com/omise/omise-go/v2"
"github.com/omise/omise-go/v2/operations"
)

func createCharge(client *omise.Client, token string, amount int64) (*omise.Charge, error) {
charge, createCharge := &omise.Charge{}, &operations.CreateCharge{
Amount: amount, // 1,000.00 THB = 100000 satang
Currency: "THB",
Card: token,
Description: "Order #1234",
Metadata: map[string]interface{}{
"order_id": "1234",
"customer_name": "John Doe",
},
}

if err := client.Do(charge, createCharge); err != nil {
return nil, fmt.Errorf("charge creation failed: %w", err)
}

if charge.Paid {
fmt.Printf("Charge successful: %s\n", charge.ID)
} else {
fmt.Printf("Charge failed: %s\n", charge.FailureMessage)
}

return charge, nil
}

ด้วย Context

import "context"

func createChargeWithContext(
ctx context.Context,
client *omise.Client,
token string,
amount int64,
) (*omise.Charge, error) {
charge, createCharge := &omise.Charge{}, &operations.CreateCharge{
Amount: amount,
Currency: "THB",
Card: token,
}

if err := client.DoWithContext(ctx, charge, createCharge); err != nil {
return nil, err
}

return charge, nil
}

// การใช้งานพร้อม timeout
func main() {
client, _ := omise.NewClient("pkey", "skey")

ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

charge, err := createChargeWithContext(ctx, client, "tokn_test_123", 100000)
if err != nil {
log.Fatal(err)
}
}

ด้วย 3D Secure

type ChargeResult struct {
Charge *omise.Charge
RedirectURI string
}

func createSecureCharge(
client *omise.Client,
token string,
amount int64,
returnURI string,
) (*ChargeResult, error) {
charge, createCharge := &omise.Charge{}, &operations.CreateCharge{
Amount: amount,
Currency: "THB",
Card: token,
ReturnURI: returnURI,
}

if err := client.Do(charge, createCharge); err != nil {
return nil, err
}

if charge.Authorized {
if charge.AuthorizeURI != "" {
return &ChargeResult{RedirectURI: charge.AuthorizeURI}, nil
}
return &ChargeResult{Charge: charge}, nil
}

return nil, fmt.Errorf("charge failed: %s", charge.FailureMessage)
}

การดึงค่าธรรมเนียม

func getCharge(client *omise.Client, chargeID string) (*omise.Charge, error) {
charge, retrieve := &omise.Charge{}, &operations.RetrieveCharge{
ChargeID: chargeID,
}

if err := client.Do(charge, retrieve); err != nil {
return nil, err
}

fmt.Printf("Amount: %d\n", charge.Amount)
fmt.Printf("Currency: %s\n", charge.Currency)
fmt.Printf("Status: %s\n", charge.Status)
fmt.Printf("Paid: %t\n", charge.Paid)

return charge, nil
}

การแสดงรายชื่อค่าธรรมเนียม

func listCharges(client *omise.Client) ([]*omise.Charge, error) {
chargeList := &omise.ChargeList{}
listOp := &operations.ListCharges{
List: operations.List{
Limit: 20,
Offset: 0,
Order: operations.ReverseChronological,
},
}

if err := client.Do(chargeList, listOp); err != nil {
return nil, err
}

return chargeList.Data, nil
}

// พร้อมการกรองตามวันที่
func listRecentCharges(client *omise.Client) ([]*omise.Charge, error) {
weekAgo := time.Now().AddDate(0, 0, -7)

chargeList := &omise.ChargeList{}
listOp := &operations.ListCharges{
List: operations.List{
From: weekAgo,
To: time.Now(),
},
}

if err := client.Do(chargeList, listOp); err != nil {
return nil, err
}

// กรองค่าธรรมเนียมที่ชำระแล้ว
var paidCharges []*omise.Charge
for _, charge := range chargeList.Data {
if charge.Paid {
paidCharges = append(paidCharges, charge)
}
}

return paidCharges, nil
}

การสร้างลูกค้า

func createCustomer(
client *omise.Client,
email, description string,
) (*omise.Customer, error) {
customer, createOp := &omise.Customer{}, &operations.CreateCustomer{
Email: email,
Description: description,
Metadata: map[string]interface{}{
"user_id": "12345",
"account_type": "premium",
},
}

if err := client.Do(customer, createOp); err != nil {
return nil, err
}

fmt.Printf("Customer created: %s\n", customer.ID)
return customer, nil
}

การบันทึกบัตรเข้าไปในลูกค้า

func addCardToCustomer(
client *omise.Client,
customerID, token string,
) (*omise.Customer, error) {
customer, updateOp := &omise.Customer{}, &operations.UpdateCustomer{
CustomerID: customerID,
Card: token,
}

if err := client.Do(customer, updateOp); err != nil {
return nil, err
}

fmt.Printf("Card saved: %s\n", customer.DefaultCard)
return customer, nil
}

// สร้างลูกค้าด้วยบัตร
func createCustomerWithCard(
client *omise.Client,
email, token string,
) (*omise.Customer, error) {
customer, createOp := &omise.Customer{}, &operations.CreateCustomer{
Email: email,
Card: token,
}

if err := client.Do(customer, createOp); err != nil {
return nil, err
}

return customer, nil
}

การแสดงรายชื่อบัตรลูกค้า

func listCustomerCards(client *omise.Client, customerID string) ([]*omise.Card, error) {
customer, retrieve := &omise.Customer{}, &operations.RetrieveCustomer{
CustomerID: customerID,
}

if err := client.Do(customer, retrieve); err != nil {
return nil, err
}

for _, card := range customer.Cards.Data {
fmt.Printf("%s ending in %s\n", card.Brand, card.LastDigits)
fmt.Printf("Expires: %d/%d\n", card.ExpirationMonth, card.ExpirationYear)
}

return customer.Cards.Data, nil
}

การสร้างการคืนเงิน

// การคืนเงินเต็มจำนวน
func refundCharge(client *omise.Client, chargeID string) (*omise.Refund, error) {
refund, createOp := &omise.Refund{}, &operations.CreateRefund{
ChargeID: chargeID,
}

if err := client.Do(refund, createOp); err != nil {
return nil, err
}

return refund, nil
}

// การคืนเงินบางส่วน
func partialRefund(
client *omise.Client,
chargeID string,
amount int64,
) (*omise.Refund, error) {
refund, createOp := &omise.Refund{}, &operations.CreateRefund{
ChargeID: chargeID,
Amount: amount,
Metadata: map[string]interface{}{
"reason": "customer_request",
"ticket_id": "TICKET-123",
},
}

if err := client.Do(refund, createOp); err != nil {
return nil, err
}

fmt.Printf("Refund %s: %d %s\n", refund.ID, refund.Amount, refund.Currency)
return refund, nil
}

การสร้างการโอน

func createTransfer(
client *omise.Client,
amount int64,
recipientID string,
) (*omise.Transfer, error) {
transfer, createOp := &omise.Transfer{}, &operations.CreateTransfer{
Amount: amount,
Recipient: recipientID,
Metadata: map[string]interface{}{
"payout_id": "PAYOUT-456",
},
}

if err := client.Do(transfer, createOp); err != nil {
return nil, err
}

fmt.Printf("Transfer %s: %d\n", transfer.ID, transfer.Amount)
return transfer, nil
}

วิธีการชำระเงินแบบอื่น

การสร้างแหล่งที่มา

// QR PromptPay
func createPromptPaySource(client *omise.Client, amount int64) (*omise.Source, error) {
source, createOp := &omise.Source{}, &operations.CreateSource{
Type: "promptpay",
Amount: amount,
Currency: "THB",
}

if err := client.Do(source, createOp); err != nil {
return nil, err
}

fmt.Printf("QR Code URL: %s\n", source.ScannableCode.Image.DownloadURI)

// สร้างค่าธรรมเนียมด้วย source
charge, chargeOp := &omise.Charge{}, &operations.CreateCharge{
Amount: amount,
Currency: "THB",
Source: source.ID,
ReturnURI: "https://example.com/payment/callback",
}

if err := client.Do(charge, chargeOp); err != nil {
return nil, err
}

return source, nil
}

ธนาคารอินเทอร์เน็ต

func createInternetBankingCharge(
client *omise.Client,
amount int64,
bank string,
) (*omise.Charge, error) {
source, sourceOp := &omise.Source{}, &operations.CreateSource{
Type: "internet_banking_" + bank,
Amount: amount,
Currency: "THB",
}

if err := client.Do(source, sourceOp); err != nil {
return nil, err
}

charge, chargeOp := &omise.Charge{}, &operations.CreateCharge{
Amount: amount,
Currency: "THB",
Source: source.ID,
ReturnURI: "https://example.com/payment/callback",
}

if err := client.Do(charge, chargeOp); err != nil {
return nil, err
}

// เปลี่ยนเส้นทางไปยัง charge.AuthorizeURI
return charge, nil
}

การผ่อนชำระ

func createInstallmentCharge(
client *omise.Client,
amount int64,
bank string,
term int,
) (*omise.Charge, error) {
source, sourceOp := &omise.Source{}, &operations.CreateSource{
Type: "installment_" + bank,
Amount: amount,
Currency: "THB",
InstallmentTerm: term,
}

if err := client.Do(source, sourceOp); err != nil {
return nil, err
}

charge, chargeOp := &omise.Charge{}, &operations.CreateCharge{
Amount: amount,
Currency: "THB",
Source: source.ID,
ReturnURI: "https://example.com/payment/callback",
}

if err := client.Do(charge, chargeOp); err != nil {
return nil, err
}

return charge, nil
}

การจัดการข้อผิดพลาด

import (
"errors"
"github.com/omise/omise-go/v2"
)

func createChargeWithErrorHandling(
client *omise.Client,
token string,
amount int64,
) (*omise.Charge, error) {
charge, createCharge := &omise.Charge{}, &operations.CreateCharge{
Amount: amount,
Currency: "THB",
Card: token,
}

if err := client.Do(charge, createCharge); err != nil {
var omiseErr *omise.Error
if errors.As(err, &omiseErr) {
switch omiseErr.Code {
case "authentication_failure":
return nil, errors.New("invalid API key")
case "invalid_card":
return nil, errors.New("card was declined")
case "insufficient_fund":
return nil, errors.New("insufficient funds")
default:
return nil, fmt.Errorf("omise error: %s", omiseErr.Message)
}
}
return nil, fmt.Errorf("request failed: %w", err)
}

return charge, nil
}

ตัวจัดการข้อผิดพลาดแบบกำหนดเอง

type PaymentErrorHandler struct {
errorMessages map[string]string
}

func NewPaymentErrorHandler() *PaymentErrorHandler {
return &PaymentErrorHandler{
errorMessages: map[string]string{
"insufficient_fund": "Insufficient funds on card",
"stolen_or_lost_card": "Card reported as stolen or lost",
"invalid_security_code": "Invalid CVV code",
"payment_cancelled": "Payment was cancelled",
},
}
}

func (h *PaymentErrorHandler) GetErrorMessage(err error) string {
var omiseErr *omise.Error
if errors.As(err, &omiseErr) {
if msg, ok := h.errorMessages[omiseErr.Code]; ok {
return msg
}
return omiseErr.Message
}
return err.Error()
}

การรวม HTTP Server

ตัวอย่าง Handler

package main

import (
"encoding/json"
"net/http"
"github.com/omise/omise-go/v2"
"github.com/omise/omise-go/v2/operations"
)

type PaymentHandler struct {
client *omise.Client
paymentบริการ *Paymentบริการ
}

func NewPaymentHandler(client *omise.Client, service *Paymentบริการ) *PaymentHandler {
return &PaymentHandler{
client: client,
paymentบริการ: service,
}
}

func (h *PaymentHandler) CreatePayment(w http.ResponseWriter, r *http.Request) {
var req struct {
OrderID string `json:"order_id"`
OmiseToken string `json:"omise_token"`
}

if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "Invalid request", http.StatusBadRequest)
return
}

order, err := h.paymentบริการ.GetOrder(req.OrderID)
if err != nil {
http.Error(w, "Order not found", http.StatusNotFound)
return
}

charge, createCharge := &omise.Charge{}, &operations.CreateCharge{
Amount: int64(order.Total * 100),
Currency: "THB",
Card: req.OmiseToken,
Description: "Order #" + order.ID,
Metadata: map[string]interface{}{
"order_id": order.ID,
"customer_email": order.Email,
},
ReturnURI: "https://example.com/payment/callback",
}

if err := h.client.Do(charge, createCharge); err != nil {
http.Error(w, "Payment failed", http.StatusInternalServerError)
return
}

// บันทึกการชำระเงิน
payment := &Payment{
OrderID: order.ID,
ChargeID: charge.ID,
Amount: order.Total,
Status: charge.Status,
Paid: charge.Paid,
}

if err := h.paymentบริการ.SavePayment(payment); err != nil {
http.Error(w, "Failed to save payment", http.StatusInternalServerError)
return
}

if charge.Paid {
h.paymentบริการ.MarkOrderPaid(order.ID)
json.NewEncoder(w).Encode(map[string]interface{}{
"success": true,
"charge": charge,
})
} else if charge.AuthorizeURI != "" {
json.NewEncoder(w).Encode(map[string]interface{}{
"redirect_uri": charge.AuthorizeURI,
})
} else {
http.Error(w, charge.FailureMessage, http.StatusBadRequest)
}
}

func (h *PaymentHandler) PaymentCallback(w http.ResponseWriter, r *http.Request) {
chargeID := r.URL.Query().Get("id")

charge, retrieve := &omise.Charge{}, &operations.RetrieveCharge{
ChargeID: chargeID,
}

if err := h.client.Do(charge, retrieve); err != nil {
http.Error(w, "Payment verification failed", http.StatusInternalServerError)
return
}

if err := h.paymentบริการ.UpdatePaymentStatus(charge.ID, charge.Status, charge.Paid); err != nil {
http.Error(w, "Failed to update payment", http.StatusInternalServerError)
return
}

if charge.Paid {
http.Redirect(w, r, "/orders/"+charge.Metadata["order_id"].(string)+"?success=true", http.StatusSeeOther)
} else {
http.Redirect(w, r, "/payment?error="+charge.FailureMessage, http.StatusSeeOther)
}
}

เลเยอร์บริการ

type Paymentบริการ struct {
client *omise.Client
db *Database
}

func NewPaymentบริการ(client *omise.Client, db *Database) *Paymentบริการ {
return &Paymentบริการ{
client: client,
db: db,
}
}

func (s *Paymentบริการ) GetOrder(orderID string) (*Order, error) {
return s.db.GetOrder(orderID)
}

func (s *Paymentบริการ) SavePayment(payment *Payment) error {
return s.db.SavePayment(payment)
}

func (s *Paymentบริการ) UpdatePaymentStatus(chargeID, status string, paid bool) error {
return s.db.UpdatePaymentStatus(chargeID, status, paid)
}

func (s *Paymentบริการ) MarkOrderPaid(orderID string) error {
return s.db.UpdateOrderStatus(orderID, "paid")
}

func (s *Paymentบริการ) ChargeCustomer(customerID string, amount int64) (*omise.Charge, error) {
charge, createCharge := &omise.Charge{}, &operations.CreateCharge{
Amount: amount,
Currency: "THB",
Customer: customerID,
}

if err := s.client.Do(charge, createCharge); err != nil {
return nil, err
}

return charge, nil
}

func (s *Paymentบริการ) RefundCharge(chargeID string, amount *int64) (*omise.Refund, error) {
refund, refundOp := &omise.Refund{}, &operations.CreateRefund{
ChargeID: chargeID,
}

if amount != nil {
refundOp.Amount = *amount
}

if err := s.client.Do(refund, refundOp); err != nil {
return nil, err
}

return refund, nil
}

การดำเนินการพร้อมกันด้วย Goroutines

การประมวลผลค่าธรรมเนียมหลายรายการ

func processChargesConcurrently(
client *omise.Client,
tokens []string,
amount int64,
) ([]*omise.Charge, []error) {
type result struct {
charge *omise.Charge
err error
}

results := make(chan result, len(tokens))

for _, token := range tokens {
go func(t string) {
charge, createCharge := &omise.Charge{}, &operations.CreateCharge{
Amount: amount,
Currency: "THB",
Card: t,
}

err := client.Do(charge, createCharge)
results <- result{charge: charge, err: err}
}(token)
}

var charges []*omise.Charge
var errors []error

for i := 0; i < len(tokens); i++ {
res := <-results
if res.err != nil {
errors = append(errors, res.err)
} else {
charges = append(charges, res.charge)
}
}

return charges, errors
}

รูปแบบ Worker Pool

type ChargeJob struct {
Token string
Amount int64
}

type ChargeWorkerPool struct {
client *omise.Client
workers int
}

func NewChargeWorkerPool(client *omise.Client, workers int) *ChargeWorkerPool {
return &ChargeWorkerPool{
client: client,
workers: workers,
}
}

func (p *ChargeWorkerPool) ProcessJobs(jobs []ChargeJob) []*omise.Charge {
jobsChan := make(chan ChargeJob, len(jobs))
resultsChan := make(chan *omise.Charge, len(jobs))

// เริ่ม workers
for i := 0; i < p.workers; i++ {
go p.worker(jobsChan, resultsChan)
}

// ส่ง jobs
for _, job := range jobs {
jobsChan <- job
}
close(jobsChan)

// รวบรวมผลลัพธ์
var charges []*omise.Charge
for i := 0; i < len(jobs); i++ {
charge := <-resultsChan
if charge != nil {
charges = append(charges, charge)
}
}

return charges
}

func (p *ChargeWorkerPool) worker(jobs <-chan ChargeJob, results chan<- *omise.Charge) {
for job := range jobs {
charge, createCharge := &omise.Charge{}, &operations.CreateCharge{
Amount: job.Amount,
Currency: "THB",
Card: job.Token,
}

if err := p.client.Do(charge, createCharge); err != nil {
results <- nil
} else {
results <- charge
}
}
}

แนวทางปฏิบัติที่ดีที่สุด

1. ใช้ Context สำหรับการควบคุม Timeout

func createChargeWithTimeout(
client *omise.Client,
token string,
amount int64,
timeout time.Duration,
) (*omise.Charge, error) {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()

charge, createCharge := &omise.Charge{}, &operations.CreateCharge{
Amount: amount,
Currency: "THB",
Card: token,
}

if err := client.DoWithContext(ctx, charge, createCharge); err != nil {
return nil, err
}

return charge, nil
}

2. ใช้งาน Idempotency

import "github.com/google/uuid"

func createIdempotentCharge(
client *omise.Client,
token string,
amount int64,
orderID string,
) (*omise.Charge, error) {
idempotencyKey := fmt.Sprintf("order-%s-%s", orderID, uuid.New().String())

charge, createCharge := &omise.Charge{}, &operations.CreateCharge{
Amount: amount,
Currency: "THB",
Card: token,
Options: operations.Options{
IdempotencyKey: idempotencyKey,
},
}

if err := client.Do(charge, createCharge); err != nil {
return nil, err
}

return charge, nil
}

3. ใช้การบันทึกแบบมีโครงสร้าง

import "log/slog"

type PaymentLogger struct {
logger *slog.Logger
}

func NewPaymentLogger() *PaymentLogger {
return &PaymentLogger{
logger: slog.Default(),
}
}

func (l *PaymentLogger) LogCharge(charge *omise.Charge, err error) {
if err != nil {
l.logger.Error("Charge failed",
slog.String("error", err.Error()),
)
return
}

l.logger.Info("Charge created",
slog.String("charge_id", charge.ID),
slog.Int64("amount", charge.Amount),
slog.String("currency", charge.Currency),
slog.Bool("paid", charge.Paid),
)
}

4. ใช้ Custom Types สำหรับเงิน

type Money struct {
Amount int64
Currency string
}

func NewMoney(amount float64, currency string) Money {
return Money{
Amount: int64(amount * 100),
Currency: currency,
}
}

func (m Money) ToFloat() float64 {
return float64(m.Amount) / 100
}

// การใช้งาน
money := NewMoney(1000.00, "THB")
charge, createCharge := &omise.Charge{}, &operations.CreateCharge{
Amount: money.Amount,
Currency: money.Currency,
Card: token,
}

5. ใช้งาน Retry Logic

import "time"

func retryOperation(
fn func() error,
maxRetries int,
baseDelay time.Duration,
) error {
for attempt := 0; attempt < maxRetries; attempt++ {
if err := fn(); err != nil {
if attempt == maxRetries-1 {
return err
}

delay := baseDelay * time.Duration(1<<uint(attempt))
time.Sleep(delay)
continue
}
return nil
}
return nil
}

// การใช้งาน
err := retryOperation(func() error {
charge, createCharge := &omise.Charge{}, &operations.CreateCharge{
Amount: 100000,
Currency: "THB",
Card: token,
}
return client.Do(charge, createCharge)
}, 3, time.Second)

6. ใช้ Interfaces สำหรับการทดสอบ

type ChargeCreator interface {
CreateCharge(token string, amount int64) (*omise.Charge, error)
}

type OmiseChargeCreator struct {
client *omise.Client
}

func (o *OmiseChargeCreator) CreateCharge(token string, amount int64) (*omise.Charge, error) {
charge, createCharge := &omise.Charge{}, &operations.CreateCharge{
Amount: amount,
Currency: "THB",
Card: token,
}

if err := o.client.Do(charge, createCharge); err != nil {
return nil, err
}

return charge, nil
}

// Mock สำหรับการทดสอบ
type MockChargeCreator struct {
MockCharge *omise.Charge
MockError error
}

func (m *MockChargeCreator) CreateCharge(token string, amount int64) (*omise.Charge, error) {
return m.MockCharge, m.MockError
}

การทดสอบ

Unit Testing ด้วย Mocks

package payment

import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/omise/omise-go/v2"
)

func TestCreateCharge(t *testing.T) {
// สร้าง mock
mockCreator := &MockChargeCreator{
MockCharge: &omise.Charge{
ID: "chrg_test_123",
Amount: 100000,
Currency: "THB",
Paid: true,
},
MockError: nil,
}

// ทดสอบ
charge, err := mockCreator.CreateCharge("tokn_test_123", 100000)

assert.NoError(t, err)
assert.NotNil(t, charge)
assert.Equal(t, "chrg_test_123", charge.ID)
assert.True(t, charge.Paid)
}

func TestCreateCharge_Error(t *testing.T) {
mockCreator := &MockChargeCreator{
MockCharge: nil,
MockError: errors.New("insufficient_fund"),
}

charge, err := mockCreator.CreateCharge("tokn_test_123", 100000)

assert.Error(t, err)
assert.Nil(t, charge)
assert.Contains(t, err.Error(), "insufficient_fund")
}

Integration Testing

func TestCreateChargeIntegration(t *testing.T) {
if testing.Short() {
t.Skip("Skipping integration test")
}

client, err := omise.NewClient(
"pkey_test_123456789",
"skey_test_123456789",
)
assert.NoError(t, err)

charge, createCharge := &omise.Charge{}, &operations.CreateCharge{
Amount: 100000,
Currency: "THB",
Card: "tokn_test_5086xl7ddjbases4sq3i",
}

err = client.Do(charge, createCharge)
assert.NoError(t, err)
assert.NotEmpty(t, charge.ID)
assert.False(t, charge.Livemode)
}

การแก้ไขปัญหา

การหมดเวลาการเชื่อมต่อ

import "time"

client, _ := omise.NewClient("pkey", "skey")
client.SetTimeout(60 * time.Second)

การบันทึก Debug

client, _ := omise.NewClient("pkey", "skey")
client.SetDebug(true) // เปิดใช้งานการบันทึก debug

การตรวจสอบลายเซ็นเว็บฮุก

import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
)

func verifyWebhookSignature(payload []byte, signature, secret string) bool {
mac := hmac.New(sha256.New, []byte(secret))
mac.Write(payload)
expectedSignature := hex.EncodeToString(mac.Sum(nil))

return hmac.Equal([]byte(signature), []byte(expectedSignature))
}

// ตัวจัดการ HTTP
func webhookHandler(w http.ResponseWriter, r *http.Request) {
body, _ := ioutil.ReadAll(r.Body)
signature := r.Header.Get("Omise-Signature")

if !verifyWebhookSignature(body, signature, os.Getenv("OMISE_WEBHOOK_SECRET")) {
http.Error(w, "Invalid signature", http.StatusUnauthorized)
return
}

// ประมวลผล webhook
w.WriteHeader(http.StatusOK)
}

คำถามที่พบบ่อย

ฉันจะจัดการ webhooks ได้อย่างไร?

type WebhookEvent struct {
Key string `json:"key"`
Data map[string]interface{} `json:"data"`
}

func handleWebhook(client *omise.Client, event WebhookEvent) error {
switch event.Key {
case "charge.complete":
return handleChargeComplete(client, event.Data["id"].(string))
case "refund.create":
return handleRefundCreate(client, event.Data)
}
return nil
}

func handleChargeComplete(client *omise.Client, chargeID string) error {
charge, retrieve := &omise.Charge{}, &operations.RetrieveCharge{
ChargeID: chargeID,
}

if err := client.Do(charge, retrieve); err != nil {
return err
}

// อัปเดตสถานะการชำระเงิน
return nil
}

ฉันจะทดสอบโดยไม่มีค่าธรรมเนียมจริงได้อย่างไร?

// ใช้ test API keys
client, _ := omise.NewClient(
"pkey_test_123456789",
"skey_test_123456789",
)

// ใช้ test tokens
charge, createCharge := &omise.Charge{}, &operations.CreateCharge{
Amount: 100000,
Currency: "THB",
Card: "tokn_test_5086xl7ddjbases4sq3i",
}

client.Do(charge, createCharge)
fmt.Println("Test mode:", !charge.Livemode)

ฉันจะจัดการการคืนเงินบางส่วนได้อย่างไร?

func refundCharge(client *omise.Client, chargeID string, amount *int64) (*omise.Refund, error) {
// รับค่าธรรมเนียมเพื่อตรวจสอบจำนวนเงินที่สามารถคืนได้
charge, retrieve := &omise.Charge{}, &operations.RetrieveCharge{
ChargeID: chargeID,
}

if err := client.Do(charge, retrieve); err != nil {
return nil, err
}

refundable := charge.Amount - charge.Refunded

if amount != nil && *amount > refundable {
return nil, errors.New("refund amount exceeds refundable amount")
}

refund, refundOp := &omise.Refund{}, &operations.CreateRefund{
ChargeID: chargeID,
}

if amount != nil {
refundOp.Amount = *amount
}

if err := client.Do(refund, refundOp); err != nil {
return nil, err
}

return refund, nil
}

ฉันจะบันทึกบัตรหลายใบสำหรับลูกค้าได้อย่างไร?

func addCardToCustomer(client *omise.Client, customerID, token string) (*omise.Customer, error) {
customer, updateOp := &omise.Customer{}, &operations.UpdateCustomer{
CustomerID: customerID,
Card: token,
}

if err := client.Do(customer, updateOp); err != nil {
return nil, err
}

return customer, nil
}

func listCustomerCards(client *omise.Client, customerID string) ([]*omise.Card, error) {
customer, retrieve := &omise.Customer{}, &operations.RetrieveCustomer{
CustomerID: customerID,
}

if err := client.Do(customer, retrieve); err != nil {
return nil, err
}

return customer.Cards.Data, nil
}

func chargeSpecificCard(
client *omise.Client,
customerID, cardID string,
amount int64,
) (*omise.Charge, error) {
charge, createCharge := &omise.Charge{}, &operations.CreateCharge{
Amount: amount,
Currency: "THB",
Customer: customerID,
Card: cardID,
}

if err := client.Do(charge, createCharge); err != nil {
return nil, err
}

return charge, nil
}

ฉันจะใช้งานการเรียกเก็บเงินแบบสมาชิกได้อย่างไร?

type SubscriptionManager struct {
client *omise.Client
}

func NewSubscriptionManager(client *omise.Client) *SubscriptionManager {
return &SubscriptionManager{client: client}
}

func (s *SubscriptionManager) ChargeMonthly(customerID string, planAmount int64) (*omise.Charge, error) {
charge, createCharge := &omise.Charge{}, &operations.CreateCharge{
Amount: planAmount,
Currency: "THB",
Customer: customerID,
Description: fmt.Sprintf("Subscription %s", time.Now().Format("January 2006")),
}

if err := s.client.Do(charge, createCharge); err != nil {
return nil, err
}

return charge, nil
}

// ด้วย cron
import "github.com/robfig/cron/v3"

func scheduleSubscriptionCharges(manager *SubscriptionManager) {
c := cron.New()
c.AddFunc("0 0 1 * *", func() { // วันแรกของทุกเดือน
// เรียกเก็บเงินสมาชิกทั้งหมด
})
c.Start()
}

ทรัพยากรที่เกี่ยวข้อง

ขั้นตอนถัดไป

การสนับสนุน

หากคุณพบปัญหากับไลบรารี Go:

  1. ตรวจสอบ GitHub Issues
  2. ตรวจสอบ เอกสาร API
  3. ติดต่อ support@omise.co พร้อม:
    • เวอร์ชัน Go
    • เวอร์ชันไลบรารี omise-go
    • ข้อความข้อผิดพลาดและ stack trace
    • ขั้นตอนในการทำซ้ำ