mirror of
https://github.com/acaloiaro/go-tremendous
synced 2026-07-21 02:09:52 +00:00
198 lines
5.9 KiB
Go
198 lines
5.9 KiB
Go
package tremendous
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// OrdersService handles the "orders" resource.
|
|
type OrdersService struct {
|
|
client *Client
|
|
}
|
|
|
|
// Order represents the JSON structure of an order in the Tremendous API.
|
|
type Order struct {
|
|
ID string `json:"id,omitempty"`
|
|
ExternalID string `json:"external_id,omitempty"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
Message string `json:"message,omitempty"`
|
|
Status string `json:"status,omitempty"`
|
|
Payment Payment `json:"payment,omitempty"`
|
|
Rewards []Reward `json:"rewards"`
|
|
}
|
|
|
|
// Payment captures details of the payment object used by the order.
|
|
type Payment struct {
|
|
FundingSourceID string `json:"funding_source_id,omitempty"`
|
|
Amount float64 `json:"amount,omitempty"`
|
|
CurrencyCode string `json:"currency_code,omitempty"`
|
|
Object string `json:"object,omitempty"`
|
|
}
|
|
|
|
// Reward captures details of a reward that has been paid out
|
|
type Reward struct {
|
|
ID string `json:"id"`
|
|
OrderID string `json:"order_id"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
Value OrderDenomination `json:"value"`
|
|
Delivery OrderDelivery `json:"delivery"`
|
|
Recipient OrderRecipient `json:"recipient"`
|
|
}
|
|
|
|
// OrderArgs is the request struct for creating an order. It wraps
|
|
// the "order" object exactly as Tremendous expects in the JSON payload.
|
|
type OrderArgs struct {
|
|
CampaignID *string `json:"campaign_id"`
|
|
ExternalID *string `json:"external_id"`
|
|
Payment OrderPaymentArg `json:"payment"`
|
|
Reward RewardArg `json:"reward"`
|
|
}
|
|
|
|
// OrderPaymentArg describes an order's payment details
|
|
type OrderPaymentArg struct {
|
|
FundingSourceID string `json:"funding_source_id,omitempty"`
|
|
}
|
|
|
|
// OrderDenomination denominates the reward's monetary value
|
|
type OrderDenomination struct {
|
|
Denomination float64 `json:"denomination"`
|
|
CurrencyCode string `json:"currency_code"`
|
|
}
|
|
|
|
// OrderDelivery designates how an order's reward is delivered
|
|
type OrderDelivery struct {
|
|
Method string `json:"method"` // email, link, or phone
|
|
Status string `json:"status"`
|
|
Link string `json:"link"`
|
|
}
|
|
|
|
// RewardArg is subset of field for on order reward
|
|
type RewardArg struct {
|
|
CampaignID *string `json:"campaign_id"`
|
|
Delivery OrderDelivery `json:"delivery"`
|
|
Products []string `json:"products"`
|
|
Recipient OrderRecipient `json:"recipient"`
|
|
Value OrderDenomination `json:"value"`
|
|
}
|
|
|
|
// OrderRecipient contains details about reward recipients
|
|
type OrderRecipient struct {
|
|
Email string `json:"email,omitempty"`
|
|
Name string `json:"name,omitempty"`
|
|
}
|
|
|
|
// singleOrderResponse models the JSON for a single order return.
|
|
type singleOrderResponse struct {
|
|
Order Order `json:"order"`
|
|
}
|
|
|
|
// listOrdersResponse models the JSON for a multi-order listing.
|
|
type listOrdersResponse struct {
|
|
Orders []Order `json:"orders"`
|
|
}
|
|
|
|
// List fetches all orders. (Query params, pagination, etc., may be added later.)
|
|
func (s *OrdersService) List() ([]Order, error) {
|
|
url := fmt.Sprintf("%s/orders", s.client.BaseURL)
|
|
|
|
req, err := http.NewRequest(http.MethodGet, url, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
req.Header = s.client.headers()
|
|
|
|
resp, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
bodyBytes, _ := io.ReadAll(resp.Body)
|
|
return nil, fmt.Errorf("unexpected status code %d: %s", resp.StatusCode, string(bodyBytes))
|
|
}
|
|
|
|
var ordersResp listOrdersResponse
|
|
if err := json.NewDecoder(resp.Body).Decode(&ordersResp); err != nil {
|
|
return nil, fmt.Errorf("failed to decode orders list: %w", err)
|
|
}
|
|
return ordersResp.Orders, nil
|
|
}
|
|
|
|
// Retrieve fetches a single order by its ID.
|
|
func (s *OrdersService) Retrieve(orderID string) (*Order, error) {
|
|
orderID = strings.TrimSpace(orderID)
|
|
url := fmt.Sprintf("%s/orders/%s", s.client.BaseURL, orderID)
|
|
|
|
req, err := http.NewRequest(http.MethodGet, url, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
req.Header = s.client.headers()
|
|
|
|
resp, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
bodyBytes, _ := io.ReadAll(resp.Body)
|
|
return nil, fmt.Errorf("unexpected status code %d: %s", resp.StatusCode, string(bodyBytes))
|
|
}
|
|
|
|
var singleResp singleOrderResponse
|
|
if err := json.NewDecoder(resp.Body).Decode(&singleResp); err != nil {
|
|
return nil, fmt.Errorf("failed to decode single order: %w", err)
|
|
}
|
|
return &singleResp.Order, nil
|
|
}
|
|
|
|
// Create sends a new order to the Tremendous API for creation.
|
|
// It requires the Payment field to be non-nil in CreateOrderArgs.
|
|
func (s *OrdersService) Create(args OrderArgs) (*Order, error) {
|
|
url := fmt.Sprintf("%s/orders", s.client.BaseURL)
|
|
|
|
// Basic validation: ensure payment is present
|
|
if args.Payment.FundingSourceID == "" {
|
|
return nil, errors.New("the 'payment.funding_source_id' field is required but was empty")
|
|
}
|
|
|
|
// Marshal the typed struct into JSON
|
|
bodyBytes, err := json.Marshal(&args)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to marshal CreateOrderArgs: %w", err)
|
|
}
|
|
|
|
log.Println("Request", string(bodyBytes))
|
|
req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(bodyBytes))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
req.Header = s.client.headers()
|
|
|
|
resp, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
// Expect 201 Created (some APIs return 200 instead).
|
|
if resp.StatusCode != http.StatusCreated && resp.StatusCode != http.StatusOK {
|
|
bodyBytes, _ := io.ReadAll(resp.Body)
|
|
return nil, fmt.Errorf("unexpected status code %d: %s", resp.StatusCode, string(bodyBytes))
|
|
}
|
|
|
|
var singleResp singleOrderResponse
|
|
if err := json.NewDecoder(resp.Body).Decode(&singleResp); err != nil {
|
|
return nil, fmt.Errorf("failed to decode create order response: %w", err)
|
|
}
|
|
return &singleResp.Order, nil
|
|
}
|