| package services |
|
|
| import ( |
| "bytes" |
| "crypto/hmac" |
| "crypto/sha256" |
| "encoding/hex" |
| "encoding/json" |
| "fmt" |
| "io" |
| "log" |
| "net/http" |
| "net/url" |
| "time" |
|
|
| "service-warungpos-go/config" |
| "service-warungpos-go/models" |
| ) |
|
|
| type TripayItem struct { |
| SKU string `json:"sku"` |
| Name string `json:"name"` |
| Price int `json:"price"` |
| Quantity int `json:"quantity"` |
| } |
|
|
| type TripayCreateRequest struct { |
| Method string `json:"method"` |
| MerchantRef string `json:"merchant_ref"` |
| Amount int `json:"amount"` |
| CustomerName string `json:"customer_name"` |
| CustomerEmail string `json:"customer_email"` |
| CustomerPhone string `json:"customer_phone"` |
| OrderItems []TripayItem `json:"order_items"` |
| ExpiredTime int64 `json:"expired_time"` |
| Signature string `json:"signature"` |
| } |
|
|
| type TripayCreateResponse struct { |
| Success bool `json:"success"` |
| Message string `json:"message"` |
| Data struct { |
| Reference string `json:"reference"` |
| MerchantRef string `json:"merchant_ref"` |
| PaymentMethod string `json:"payment_method"` |
| PaymentName string `json:"payment_name"` |
| Amount int `json:"amount"` |
| TotalFee int `json:"total_fee"` |
| FeeMerchant int `json:"fee_merchant"` |
| FeeCustomer int `json:"fee_customer"` |
| TotalAmount int `json:"amount_received"` |
| CheckoutURL string `json:"checkout_url"` |
| QrURL string `json:"qr_url"` |
| QrString string `json:"qr_string"` |
| PaymentCode string `json:"payment_code"` |
| Status string `json:"status"` |
| ExpiredTime int64 `json:"expired_time"` |
| } `json:"data"` |
| } |
|
|
| func getTripayBaseURL() string { |
| |
| return "https://tripay.co.id/api" |
| } |
|
|
| |
| func CalculateSignature(merchantRef string, amount int, customMerchantCode string, customPrivateKey string) string { |
| merchantCode := customMerchantCode |
| if merchantCode == "" { |
| merchantCode = config.GlobalConfig.TripayMerchantCode |
| } |
| privateKey := customPrivateKey |
| if privateKey == "" { |
| privateKey = config.GlobalConfig.TripayPrivateKey |
| } |
|
|
| |
| data := merchantCode + merchantRef + fmt.Sprintf("%d", amount) |
|
|
| h := hmac.New(sha256.New, []byte(privateKey)) |
| h.Write([]byte(data)) |
| return hex.EncodeToString(h.Sum(nil)) |
| } |
|
|
| |
| func CreateTripayTransaction( |
| orderNumber string, |
| amount int, |
| paymentMethod string, |
| customerName string, |
| customerEmail string, |
| customerPhone string, |
| items []TripayItem, |
| companyID uint, |
| ) (*TripayCreateResponse, error) { |
| |
| var setting models.Setting |
| config.DB.First(&setting, 1) |
|
|
| |
| dbPayMethod := setting.TripayPaymentMethod |
| if paymentMethod == "" || paymentMethod == "QRIS" { |
| if dbPayMethod != "" { |
| paymentMethod = dbPayMethod |
| } else { |
| paymentMethod = "QRIS" |
| } |
| } |
|
|
| if customerName == "" { |
| customerName = "Customer" |
| } |
| if customerEmail == "" { |
| customerEmail = "customer@mail.com" |
| } |
| if customerPhone == "" { |
| customerPhone = "081234567890" |
| } |
|
|
| mCode := setting.TripayMerchantCode |
| if mCode == "" { |
| mCode = config.GlobalConfig.TripayMerchantCode |
| } |
| apiKey := setting.TripayAPIKey |
| if apiKey == "" { |
| apiKey = config.GlobalConfig.TripayAPIKey |
| } |
| privKey := setting.TripayPrivateKey |
| if privKey == "" { |
| privKey = config.GlobalConfig.TripayPrivateKey |
| } |
| proxyURLStr := setting.TripayProxyURL |
| if proxyURLStr == "" { |
| proxyURLStr = config.GlobalConfig.TripayProxyURL |
| } |
|
|
| |
| transport := &http.Transport{} |
| if proxyURLStr != "" { |
| proxyURL, err := url.Parse(proxyURLStr) |
| if err == nil { |
| transport.Proxy = http.ProxyURL(proxyURL) |
| log.Printf("[TRIPAY] Menggunakan proxy outbound: %s", proxyURLStr) |
| } else { |
| log.Printf("[TRIPAY] Gagal memproses format proxy URL: %v", err) |
| } |
| } |
|
|
| client := &http.Client{ |
| Transport: transport, |
| Timeout: 15 * time.Second, |
| } |
|
|
| |
| methodsToTry := []string{paymentMethod} |
| if paymentMethod == "QRIS" { |
| methodsToTry = append(methodsToTry, "QRISC") |
| } else if paymentMethod == "QRISC" { |
| methodsToTry = append(methodsToTry, "QRIS") |
| } |
|
|
| var lastError error |
| var tripayResp TripayCreateResponse |
|
|
| for _, currentMethod := range methodsToTry { |
| |
| signature := CalculateSignature(orderNumber, amount, mCode, privKey) |
|
|
| |
| expiredUnix := time.Now().Add(5 * time.Minute).Unix() |
|
|
| |
| payload := TripayCreateRequest{ |
| Method: currentMethod, |
| MerchantRef: orderNumber, |
| Amount: amount, |
| CustomerName: customerName, |
| CustomerEmail: customerEmail, |
| CustomerPhone: customerPhone, |
| OrderItems: items, |
| ExpiredTime: expiredUnix, |
| Signature: signature, |
| } |
|
|
| jsonBytes, err := json.Marshal(payload) |
| if err != nil { |
| return nil, fmt.Errorf("gagal merancang payload Tripay: %v", err) |
| } |
|
|
| apiURL := getTripayBaseURL() + "/transaction/create" |
| log.Printf("[TRIPAY] Mengirim request tagihan ke: %s (Method=%s, CompanyID=%d)", apiURL, currentMethod, companyID) |
|
|
| req, err := http.NewRequest("POST", apiURL, bytes.NewBuffer(jsonBytes)) |
| if err != nil { |
| return nil, fmt.Errorf("gagal membuat http request: %v", err) |
| } |
|
|
| |
| req.Header.Set("Content-Type", "application/json") |
| req.Header.Set("Authorization", "Bearer "+apiKey) |
|
|
| resp, err := client.Do(req) |
| if err != nil { |
| lastError = fmt.Errorf("koneksi HTTP ke Tripay gagal: %v", err) |
| continue |
| } |
|
|
| bodyBytes, err := io.ReadAll(resp.Body) |
| resp.Body.Close() |
| if err != nil { |
| lastError = fmt.Errorf("gagal membaca response body Tripay: %v", err) |
| continue |
| } |
|
|
| if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated { |
| log.Printf("[TRIPAY] Response error: Status=%d, Body=%s", resp.StatusCode, string(bodyBytes)) |
| |
| |
| var errorResp struct { |
| Success bool `json:"success"` |
| Message string `json:"message"` |
| } |
| _ = json.Unmarshal(bodyBytes, &errorResp) |
| |
| lastError = fmt.Errorf("tripay mengembalikan status error %d: %s", resp.StatusCode, errorResp.Message) |
| continue |
| } |
|
|
| if err := json.Unmarshal(bodyBytes, &tripayResp); err != nil { |
| lastError = fmt.Errorf("gagal parse JSON response Tripay: %v", err) |
| continue |
| } |
|
|
| if !tripayResp.Success { |
| lastError = fmt.Errorf("tripay gagal memproses: %s", tripayResp.Message) |
| continue |
| } |
|
|
| log.Printf("[TRIPAY] Sukses membuat transaksi dengan Method=%s! Ref=%s, URL=%s", currentMethod, tripayResp.Data.Reference, tripayResp.Data.CheckoutURL) |
| return &tripayResp, nil |
| } |
|
|
| return nil, lastError |
| } |
|
|
| |
| func VerifyTripaySignature(rawJSONBody []byte, receivedSignature string) bool { |
| var setting models.Setting |
| config.DB.First(&setting, 1) |
|
|
| privateKey := setting.TripayPrivateKey |
| if privateKey == "" { |
| privateKey = config.GlobalConfig.TripayPrivateKey |
| } |
|
|
| |
| h := hmac.New(sha256.New, []byte(privateKey)) |
| h.Write(rawJSONBody) |
| expectedSignature := hex.EncodeToString(h.Sum(nil)) |
|
|
| return hmac.Equal([]byte(receivedSignature), []byte(expectedSignature)) |
| } |
|
|