package arifpay import ( "bytes" "context" "encoding/json" "fmt" "io" "net/http" "time" "github.com/AnaniyaBelew/ArifpayGoPlugin" "github.com/SamuelTariku/FortuneBet-Backend/internal/config" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet" "github.com/google/uuid" ) type ArifpayService struct { cfg *config.Config transferStore wallet.TransferStore httpClient *http.Client } func NewArifpayService(cfg *config.Config, transferStore wallet.TransferStore, httpClient *http.Client) *ArifpayService { return &ArifpayService{ cfg: cfg, transferStore: transferStore, httpClient: httpClient, } } func (s *ArifpayService) CreateCheckoutSession(req domain.CreateCheckoutSessionRequest) (string, error) { // Create SDK-compatible payload paymentPayload := ArifpayGoPlugin.PaymentRequest{ CancelUrl: s.cfg.ARIFPAY.CancelUrl, Phone: req.Phone, Email: req.Email, Nonce: req.Nonce, ErrorUrl: s.cfg.ARIFPAY.ErrorUrl, NotifyUrl: s.cfg.ARIFPAY.NotifyUrl, SuccessUrl: s.cfg.ARIFPAY.SuccessUrl, PaymentMethods: req.PaymentMethods, Lang: req.Lang, } // Convert items for _, item := range req.Items { paymentPayload.Items = append(paymentPayload.Items, domain.Item{ Name: item.Name, Quantity: item.Quantity, Price: item.Price, Description: item.Description, Image: item.Image, }) } // Convert beneficiaries for _, b := range req.Beneficiaries { paymentPayload.Beneficiaries = append(paymentPayload.Beneficiaries, domain.Beneficiary{ AccountNumber: b.AccountNumber, Bank: b.Bank, Amount: b.Amount, }) } // Instantiate payment client expireDate := time.Now().AddDate(2, 0, 0) // 2 months from now paymentClient := ArifpayGoPlugin.NewPayment(s.cfg.ARIFPAY.APIKey, expireDate) // Create checkout session response, err := paymentClient.MakePayment(paymentPayload) if err != nil { return "", err } transfer := domain.CreateTransfer{ Amount: domain.Currency(req.Beneficiaries[0].Amount), Verified: false, Type: domain.DEPOSIT, ReferenceNumber: uuid.NewString(), Status: string(domain.PaymentStatusPending), } if _, err := s.transferStore.CreateTransfer(context.Background(), transfer); err != nil { return "", err } return response, nil } func (s *ArifpayService) B2CTransfer(ctx context.Context, req domain.ArifPayB2CRequest, endpoint string) (*map[string]interface{}, error) { // endpoint := c.baseURL + "/api/Telebirr/b2c/transfer" payloadBytes, err := json.Marshal(req) if err != nil { return nil, fmt.Errorf("failed to marshal request payload: %w", err) } httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, bytes.NewBuffer(payloadBytes)) if err != nil { return nil, fmt.Errorf("failed to create request: %w", err) } httpReq.Header.Set("Content-Type", "application/json") httpReq.Header.Set("Accept", "application/json") httpReq.Header.Set("x-arifpay-key", s.cfg.ARIFPAY.APIKey) resp, err := s.httpClient.Do(httpReq) if err != nil { return nil, fmt.Errorf("request to Telebirr B2C failed: %w", err) } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) if resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("Telebirr API returned status %d: %s", resp.StatusCode, string(body)) } var response map[string]interface{} if err := json.Unmarshal(body, &response); err != nil { return nil, fmt.Errorf("failed to parse response body: %w", err) } return &response, nil } func (s *ArifpayService) VerifyByTransactionID(transactionID string, paymentType int) ([]byte, error) { url := "https://gateway.arifpay.org/api/checkout/getSessionByTransactionId" var payload domain.ArifpayVerifyByTransactionIDRequest bodyBytes, err := json.Marshal(payload) if err != nil { return nil, fmt.Errorf("failed to marshal request body: %w", err) } req, err := http.NewRequest("POST", url, bytes.NewBuffer(bodyBytes)) if err != nil { return nil, fmt.Errorf("failed to create request: %w", err) } req.Header.Set("Content-Type", "application/json") req.Header.Set("x-arifpay-key", s.cfg.ARIFPAY.APIKey) resp, err := s.httpClient.Do(req) if err != nil { return nil, fmt.Errorf("request failed: %w", err) } defer resp.Body.Close() respBody, err := io.ReadAll(resp.Body) if err != nil { return nil, fmt.Errorf("failed to read response body: %w", err) } if resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("non-200 response from Arifpay: %s", string(respBody)) } return respBody, nil } func (s *ArifpayService) VerifyBySessionID(sessionID string) ([]byte, error) { url := "https://gateway.arifpay.org/api/ms/transaction/status/" + sessionID // Create GET request without body req, err := http.NewRequest("GET", url, nil) if err != nil { return nil, fmt.Errorf("failed to create request: %w", err) } req.Header.Set("Content-Type", "application/json") req.Header.Set("x-arifpay-key", s.cfg.ARIFPAY.APIKey) resp, err := s.httpClient.Do(req) if err != nil { return nil, fmt.Errorf("request failed: %w", err) } defer resp.Body.Close() respBody, err := io.ReadAll(resp.Body) if err != nil { return nil, fmt.Errorf("failed to read response body: %w", err) } if resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("non-200 response from Arifpay: %s", string(respBody)) } return respBody, nil }