reevit

package module
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 24, 2025 License: MIT Imports: 8 Imported by: 0

README

Reevit Go SDK

The official Go SDK for Reevit — a unified payment orchestration platform for Africa.

Go Reference

Installation

go get github.com/Reevit-Platform/go-sdk

Quick Start

package main

import (
	"context"
	"fmt"
	"log"

	reevit "github.com/Reevit-Platform/go-sdk"
)

func main() {
	// Initialize the client
	client := reevit.NewClient("pfk_live_xxx")

	// Create a payment intent
	payment, err := client.Payments.CreateIntent(context.Background(), &reevit.PaymentIntentRequest{
		Amount:   45000,
		Currency: "GHS",
		Method:   "momo",
		Country:  "GH",
		Metadata: map[string]interface{}{
			"order_id": "12345",
		},
	})
	if err != nil {
		log.Fatal(err)
	}

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

Services

  • Payments: client.Payments (CreateIntent, Get, List, Refund)
  • Connections: client.Connections (Create, List, Test)
  • Subscriptions: client.Subscriptions (Create, List)
  • Fraud: client.Fraud (Get, Update)

Webhook Verification

Reevit sends webhooks to notify your application of payment events. Always verify webhook signatures.

Understanding Webhooks

There are two types of webhooks in Reevit:

  1. Inbound Webhooks (PSP → Reevit): Webhooks from payment providers (Paystack, Flutterwave, etc.) to Reevit. Configure these in the PSP dashboard. Reevit handles them automatically.

  2. Outbound Webhooks (Reevit → Your App): Webhooks from Reevit to your application. Configure in Reevit Dashboard and create a handler in your app.

Signature Format
  • Header: X-Reevit-Signature: sha256=<hex-signature>
  • Signature: HMAC-SHA256(request_body, signing_secret)
Getting Your Signing Secret
  1. Go to Reevit Dashboard > Developers > Webhooks
  2. Configure your webhook endpoint URL
  3. Copy the signing secret (starts with whsec_)
  4. Set environment variable: REEVIT_WEBHOOK_SECRET=whsec_xxx...
Webhook Handler Example
package main

import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"io"
	"log"
	"net/http"
	"os"
	"strings"
)

// PaymentData represents payment event data
type PaymentData struct {
	ID         string            `json:"id"`
	Status     string            `json:"status"`
	Amount     int64             `json:"amount"`
	Currency   string            `json:"currency"`
	Provider   string            `json:"provider"`
	CustomerID string            `json:"customer_id,omitempty"`
	Metadata   map[string]string `json:"metadata,omitempty"`
}

// SubscriptionData represents subscription event data
type SubscriptionData struct {
	ID            string `json:"id"`
	CustomerID    string `json:"customer_id"`
	PlanID        string `json:"plan_id"`
	Status        string `json:"status"`
	Amount        int64  `json:"amount"`
	Currency      string `json:"currency"`
	Interval      string `json:"interval"`
	NextRenewalAt string `json:"next_renewal_at,omitempty"`
}

// WebhookPayload represents the webhook event structure
type WebhookPayload struct {
	ID        string          `json:"id"`
	Type      string          `json:"type"`
	OrgID     string          `json:"org_id"`
	CreatedAt string          `json:"created_at"`
	Data      json.RawMessage `json:"data,omitempty"`
	Message   string          `json:"message,omitempty"`
}

// VerifySignature verifies the webhook signature using HMAC-SHA256
func VerifySignature(payload []byte, signature, secret string) bool {
	if !strings.HasPrefix(signature, "sha256=") {
		return false
	}

	mac := hmac.New(sha256.New, []byte(secret))
	mac.Write(payload)
	expected := hex.EncodeToString(mac.Sum(nil))
	received := signature[7:] // Remove "sha256=" prefix

	return hmac.Equal([]byte(received), []byte(expected))
}

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

	// Get signature and secret
	signature := r.Header.Get("X-Reevit-Signature")
	secret := os.Getenv("REEVIT_WEBHOOK_SECRET")

	// Verify signature (required in production)
	if secret != "" && !VerifySignature(body, signature, secret) {
		log.Println("[Webhook] Invalid signature")
		http.Error(w, "Invalid signature", http.StatusUnauthorized)
		return
	}

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

	log.Printf("[Webhook] Received: %s (%s)", event.Type, event.ID)

	// Handle different event types
	switch event.Type {
	// Test event
	case "reevit.webhook.test":
		log.Printf("[Webhook] Test received: %s", event.Message)

	// Payment events
	case "payment.succeeded":
		var data PaymentData
		if err := json.Unmarshal(event.Data, &data); err == nil {
			handlePaymentSucceeded(data)
		}

	case "payment.failed":
		var data PaymentData
		if err := json.Unmarshal(event.Data, &data); err == nil {
			handlePaymentFailed(data)
		}

	case "payment.refunded":
		var data PaymentData
		if err := json.Unmarshal(event.Data, &data); err == nil {
			handlePaymentRefunded(data)
		}

	case "payment.pending":
		var data PaymentData
		if err := json.Unmarshal(event.Data, &data); err == nil {
			log.Printf("[Webhook] Payment pending: %s", data.ID)
		}

	// Subscription events
	case "subscription.created":
		var data SubscriptionData
		if err := json.Unmarshal(event.Data, &data); err == nil {
			handleSubscriptionCreated(data)
		}

	case "subscription.renewed":
		var data SubscriptionData
		if err := json.Unmarshal(event.Data, &data); err == nil {
			handleSubscriptionRenewed(data)
		}

	case "subscription.canceled":
		var data SubscriptionData
		if err := json.Unmarshal(event.Data, &data); err == nil {
			handleSubscriptionCanceled(data)
		}

	default:
		log.Printf("[Webhook] Unhandled event: %s", event.Type)
	}

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

// Payment handlers
func handlePaymentSucceeded(data PaymentData) {
	orderID := data.Metadata["order_id"]
	log.Printf("[Webhook] Payment succeeded: %s for order %s", data.ID, orderID)

	// TODO: Implement your business logic
	// - Update order status to "paid"
	// - Send confirmation email to customer
	// - Trigger fulfillment process
}

func handlePaymentFailed(data PaymentData) {
	log.Printf("[Webhook] Payment failed: %s", data.ID)

	// TODO: Implement your business logic
	// - Update order status to "payment_failed"
	// - Send notification to customer
	// - Allow retry
}

func handlePaymentRefunded(data PaymentData) {
	orderID := data.Metadata["order_id"]
	log.Printf("[Webhook] Payment refunded: %s for order %s", data.ID, orderID)

	// TODO: Implement your business logic
	// - Update order status to "refunded"
	// - Restore inventory if applicable
}

// Subscription handlers
func handleSubscriptionCreated(data SubscriptionData) {
	log.Printf("[Webhook] Subscription created: %s for customer %s", data.ID, data.CustomerID)

	// TODO: Implement your business logic
	// - Grant access to subscription features
	// - Send welcome email
}

func handleSubscriptionRenewed(data SubscriptionData) {
	log.Printf("[Webhook] Subscription renewed: %s", data.ID)

	// TODO: Implement your business logic
	// - Extend access period
	// - Send renewal confirmation
}

func handleSubscriptionCanceled(data SubscriptionData) {
	log.Printf("[Webhook] Subscription canceled: %s", data.ID)

	// TODO: Implement your business logic
	// - Revoke access at end of billing period
	// - Send cancellation confirmation
}

func main() {
	http.HandleFunc("/webhooks/reevit", webhookHandler)
	log.Println("Webhook server listening on :8080")
	log.Fatal(http.ListenAndServe(":8080", nil))
}
Using the webhooks Subpackage

For convenience, use the webhooks subpackage:

import "github.com/Reevit-Platform/go-sdk/webhooks"

// Verify signature
isValid := webhooks.VerifySignature(body, signature, secret)

Environment Variables

export REEVIT_API_KEY=pfk_live_xxx
export REEVIT_ORG_ID=org_xxx
export REEVIT_WEBHOOK_SECRET=whsec_xxx  # Get from Dashboard > Developers > Webhooks

Release Notes

v0.3.0
  • Updated API client to connect to production and sandbox URLs based on API key
  • Added Bearer authentication headers for secure API communication
  • Added Get, Confirm, and Cancel methods to the payments service
  • Removed orgID parameter from client initialization (simplified API)
  • Added .gitignore file to exclude unnecessary files from version control
  • Updated README.md with corrected quick start example

Support

License

MIT License - see LICENSE for details.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type APIError

type APIError struct {
	StatusCode int
	Message    string
}

APIError represents a Reevit API error.

func (*APIError) Error

func (e *APIError) Error() string

type Client

type Client struct {

	// Services
	Payments      *PaymentsService
	Connections   *ConnectionsService
	Subscriptions *SubscriptionsService
	Fraud         *FraudService
	// contains filtered or unexported fields
}

Client is the Reevit API client.

func NewClient

func NewClient(apiKey, orgID string, opts ...Option) *Client

NewClient returns a new Reevit API client.

type Connection

type Connection struct {
	ID           string                 `json:"id"`
	Provider     string                 `json:"provider"`
	Mode         string                 `json:"mode"`
	Status       string                 `json:"status"`
	Capabilities map[string]interface{} `json:"capabilities"`
	RoutingHints *RoutingHints          `json:"routing_hints"`
	Labels       []string               `json:"labels"`
}

Connection represents a connection object.

type ConnectionRequest

type ConnectionRequest struct {
	Provider     string                 `json:"provider"`
	Mode         string                 `json:"mode"`
	Credentials  map[string]interface{} `json:"credentials"`
	Capabilities map[string]interface{} `json:"capabilities,omitempty"`
	RoutingHints *RoutingHints          `json:"routing_hints,omitempty"`
	Labels       []string               `json:"labels,omitempty"`
}

ConnectionRequest represents a request to create a connection.

type ConnectionsService

type ConnectionsService service

ConnectionsService handles communication with the connection related methods of the Reevit API.

func (*ConnectionsService) Create

Create creates a new connection.

API Docs: POST /v1/connections

func (*ConnectionsService) List

List returns a list of connections.

API Docs: GET /v1/connections

func (*ConnectionsService) Test

Test tests a connection.

API Docs: POST /v1/connections/test

type FraudPolicy

type FraudPolicy struct {
	Prefer               []string `json:"prefer"`
	MaxAmount            int64    `json:"max_amount"`
	BlockedBins          []string `json:"blocked_bins"`
	AllowedBins          []string `json:"allowed_bins"`
	VelocityMaxPerMinute int      `json:"velocity_max_per_minute"`
}

FraudPolicy represents the fraud policy configuration.

type FraudPolicyInput

type FraudPolicyInput struct {
	Prefer               []string `json:"prefer,omitempty"`
	MaxAmount            int64    `json:"max_amount,omitempty"`
	BlockedBins          []string `json:"blocked_bins,omitempty"`
	AllowedBins          []string `json:"allowed_bins,omitempty"`
	VelocityMaxPerMinute int      `json:"velocity_max_per_minute,omitempty"`
}

FraudPolicyInput represents the fraud policy configuration for a payment.

type FraudService

type FraudService service

FraudService handles communication with the fraud policy related methods of the Reevit API.

func (*FraudService) Get

func (s *FraudService) Get(ctx context.Context) (*FraudPolicy, error)

Get retrieves the current fraud policy.

API Docs: GET /v1/policies/fraud

func (*FraudService) Update

func (s *FraudService) Update(ctx context.Context, policy *FraudPolicy) (*FraudPolicy, error)

Update updates the fraud policy.

API Docs: POST /v1/policies/fraud

type Option

type Option func(*Client)

Option is a functional option for configuring the Client.

func WithBaseURL

func WithBaseURL(url string) Option

WithBaseURL sets the base URL for the API.

func WithHTTPClient

func WithHTTPClient(httpClient *http.Client) Option

WithHTTPClient sets the HTTP client used for requests.

type Payment

type Payment struct {
	ID            string                 `json:"id"`
	ConnectionID  string                 `json:"connection_id"`
	Provider      string                 `json:"provider"`
	ProviderRefID string                 `json:"provider_ref_id"`
	Method        string                 `json:"method"`
	Status        string                 `json:"status"`
	Amount        int64                  `json:"amount"`
	Currency      string                 `json:"currency"`
	FeeAmount     int64                  `json:"fee_amount"`
	FeeCurrency   string                 `json:"fee_currency"`
	NetAmount     int64                  `json:"net_amount"`
	CustomerID    string                 `json:"customer_id"`
	Metadata      map[string]interface{} `json:"metadata"`
	Route         []PaymentRouteAttempt  `json:"route"`
	CreatedAt     time.Time              `json:"created_at"`
	UpdatedAt     time.Time              `json:"updated_at"`
}

Payment represents a payment object.

type PaymentIntentRequest

type PaymentIntentRequest struct {
	Amount     int64                  `json:"amount"`
	Currency   string                 `json:"currency"`
	Method     string                 `json:"method"`
	Country    string                 `json:"country"`
	CustomerID string                 `json:"customer_id,omitempty"`
	Policy     *FraudPolicyInput      `json:"policy,omitempty"`
	Metadata   map[string]interface{} `json:"metadata,omitempty"`
}

PaymentIntentRequest represents a request to create a payment intent.

type PaymentRouteAttempt

type PaymentRouteAttempt struct {
	ConnectionID string        `json:"connection_id"`
	Provider     string        `json:"provider"`
	Status       string        `json:"status"`
	Error        string        `json:"error"`
	Labels       []string      `json:"labels"`
	RoutingHints *RoutingHints `json:"routing_hints"`
}

PaymentRouteAttempt represents a routing attempt.

type PaymentSummary

type PaymentSummary struct {
	ID           string                 `json:"id"`
	ConnectionID string                 `json:"connection_id"`
	Provider     string                 `json:"provider"`
	Method       string                 `json:"method"`
	Status       string                 `json:"status"`
	Amount       int64                  `json:"amount"`
	Currency     string                 `json:"currency"`
	FeeAmount    int64                  `json:"fee_amount"`
	FeeCurrency  string                 `json:"fee_currency"`
	NetAmount    int64                  `json:"net_amount"`
	CustomerID   string                 `json:"customer_id"`
	Metadata     map[string]interface{} `json:"metadata"`
	CreatedAt    time.Time              `json:"created_at"`
}

PaymentSummary represents a summary of a payment object.

type PaymentsService

type PaymentsService service

PaymentsService handles communication with the payment related methods of the Reevit API.

func (*PaymentsService) Cancel

func (s *PaymentsService) Cancel(ctx context.Context, paymentID string) (*Payment, error)

Cancel cancels a payment intent.

API Docs: POST /v1/payments/{id}/cancel

func (*PaymentsService) Confirm

func (s *PaymentsService) Confirm(ctx context.Context, paymentID string) (*Payment, error)

Confirm confirms a payment after PSP callback.

API Docs: POST /v1/payments/{id}/confirm

func (*PaymentsService) CreateIntent

func (s *PaymentsService) CreateIntent(ctx context.Context, req *PaymentIntentRequest, opts ...RequestOption) (*Payment, error)

CreateIntent creates a new payment intent.

API Docs: POST /v1/payments/intents

func (*PaymentsService) Get

func (s *PaymentsService) Get(ctx context.Context, paymentID string) (*Payment, error)

Get retrieves a payment by ID.

API Docs: GET /v1/payments/{id}

func (*PaymentsService) List

func (s *PaymentsService) List(ctx context.Context, limit, offset int) ([]PaymentSummary, error)

List returns a list of payments.

API Docs: GET /v1/payments

type RequestOption

type RequestOption func(*http.Request)

RequestOption is a functional option for configuring API requests.

func WithIdempotencyKey

func WithIdempotencyKey(key string) RequestOption

WithIdempotencyKey sets the idempotency key for the request.

type RoutingHints

type RoutingHints struct {
	CountryPreference []string          `json:"country_preference"`
	MethodBias        map[string]string `json:"method_bias"`
	FallbackOnly      bool              `json:"fallback_only"`
}

RoutingHints represents routing preferences.

type Subscription

type Subscription struct {
	ID            string                 `json:"id"`
	OrgID         string                 `json:"org_id"`
	CustomerID    string                 `json:"customer_id"`
	PlanID        string                 `json:"plan_id"`
	Amount        int64                  `json:"amount"`
	Currency      string                 `json:"currency"`
	Method        string                 `json:"method"`
	Interval      string                 `json:"interval"`
	Status        string                 `json:"status"` // active, paused, canceled
	NextRenewalAt time.Time              `json:"next_renewal_at"`
	Metadata      map[string]interface{} `json:"metadata"`
	CreatedAt     time.Time              `json:"created_at"`
	UpdatedAt     time.Time              `json:"updated_at"`
}

Subscription represents a subscription object.

type SubscriptionRequest

type SubscriptionRequest struct {
	CustomerID string                 `json:"customer_id"`
	PlanID     string                 `json:"plan_id"`
	Amount     int64                  `json:"amount"`
	Currency   string                 `json:"currency"`
	Method     string                 `json:"method"`
	Interval   string                 `json:"interval"` // monthly, yearly
	Metadata   map[string]interface{} `json:"metadata,omitempty"`
}

SubscriptionRequest represents a request to create a subscription.

type SubscriptionsService

type SubscriptionsService service

SubscriptionsService handles communication with the subscription related methods of the Reevit API.

func (*SubscriptionsService) Create

Create creates a new subscription.

API Docs: POST /v1/subscriptions

func (*SubscriptionsService) List

List returns a list of subscriptions.

API Docs: GET /v1/subscriptions

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL