#
tokens: 45529/50000 8/59 files (page 2/4)
lines: off (toggle) GitHub
raw markdown copy
This is page 2 of 4. Use http://codebase.md/razorpay/razorpay-mcp-server?lines=false&page={x} to view the full context.

# Directory Structure

```
├── .cursor
│   └── rules
│       └── new-tool-from-docs.mdc
├── .cursorignore
├── .dockerignore
├── .github
│   ├── ISSUE_TEMPLATE
│   │   ├── bug_report.md
│   │   ├── config.yml
│   │   └── feature_request.md
│   ├── pull_request_template.md
│   └── workflows
│       ├── assign.yml
│       ├── build.yml
│       ├── ci.yml
│       ├── docker-publish.yml
│       ├── lint.yml
│       └── release.yml
├── .gitignore
├── .golangci.yaml
├── .goreleaser.yaml
├── cmd
│   └── razorpay-mcp-server
│       ├── main.go
│       └── stdio.go
├── codecov.yml
├── CONTRIBUTING.md
├── Dockerfile
├── go.mod
├── go.sum
├── LICENSE
├── Makefile
├── pkg
│   ├── contextkey
│   │   └── context_key.go
│   ├── log
│   │   ├── config.go
│   │   ├── log.go
│   │   ├── slog_test.go
│   │   └── slog.go
│   ├── mcpgo
│   │   ├── README.md
│   │   ├── server.go
│   │   ├── stdio.go
│   │   ├── tool.go
│   │   └── transport.go
│   ├── observability
│   │   └── observability.go
│   ├── razorpay
│   │   ├── mock
│   │   │   ├── server_test.go
│   │   │   └── server.go
│   │   ├── orders_test.go
│   │   ├── orders.go
│   │   ├── payment_links_test.go
│   │   ├── payment_links.go
│   │   ├── payments_test.go
│   │   ├── payments.go
│   │   ├── payouts_test.go
│   │   ├── payouts.go
│   │   ├── qr_codes_test.go
│   │   ├── qr_codes.go
│   │   ├── README.md
│   │   ├── refunds_test.go
│   │   ├── refunds.go
│   │   ├── server.go
│   │   ├── settlements_test.go
│   │   ├── settlements.go
│   │   ├── test_helpers.go
│   │   ├── tokens_test.go
│   │   ├── tokens.go
│   │   ├── tools_params_test.go
│   │   ├── tools_params.go
│   │   ├── tools_test.go
│   │   └── tools.go
│   └── toolsets
│       └── toolsets.go
├── README.md
└── SECURITY.md
```

# Files

--------------------------------------------------------------------------------
/pkg/mcpgo/tool.go:
--------------------------------------------------------------------------------

```go
package mcpgo

import (
	"context"
	"encoding/json"

	"github.com/mark3labs/mcp-go/mcp"
	"github.com/mark3labs/mcp-go/server"
)

// ToolHandler handles tool calls
type ToolHandler func(
	ctx context.Context,
	request CallToolRequest) (*ToolResult, error)

// CallToolRequest represents a request to call a tool
type CallToolRequest struct {
	Name      string
	Arguments any
}

// ToolResult represents the result of a tool call
type ToolResult struct {
	Text    string
	IsError bool
	Content []interface{}
}

// Tool represents a tool that can be added to the server
type Tool interface {
	// internal method to convert to mcp's ServerTool
	toMCPServerTool() server.ServerTool

	// GetHandler internal method for fetching the underlying handler
	GetHandler() ToolHandler
}

// PropertyOption represents a customization option for
// a parameter's schema
type PropertyOption func(schema map[string]interface{})

// Min sets the minimum value for a number parameter or
// minimum length for a string
func Min(value float64) PropertyOption {
	return func(schema map[string]interface{}) {
		propType, ok := schema["type"].(string)
		if !ok {
			return
		}

		switch propType {
		case "number", "integer":
			schema["minimum"] = value
		case "string":
			schema["minLength"] = int(value)
		case "array":
			schema["minItems"] = int(value)
		}
	}
}

// Max sets the maximum value for a number parameter or
// maximum length for a string
func Max(value float64) PropertyOption {
	return func(schema map[string]interface{}) {
		propType, ok := schema["type"].(string)
		if !ok {
			return
		}

		switch propType {
		case "number", "integer":
			schema["maximum"] = value
		case "string":
			schema["maxLength"] = int(value)
		case "array":
			schema["maxItems"] = int(value)
		}
	}
}

// Pattern sets a regex pattern for string validation
func Pattern(pattern string) PropertyOption {
	return func(schema map[string]interface{}) {
		propType, ok := schema["type"].(string)
		if !ok || propType != "string" {
			return
		}
		schema["pattern"] = pattern
	}
}

// Enum sets allowed values for a parameter
func Enum(values ...interface{}) PropertyOption {
	return func(schema map[string]interface{}) {
		schema["enum"] = values
	}
}

// DefaultValue sets a default value for a parameter
func DefaultValue(value interface{}) PropertyOption {
	return func(schema map[string]interface{}) {
		schema["default"] = value
	}
}

// MaxProperties sets the maximum number of properties for an object
func MaxProperties(max int) PropertyOption {
	return func(schema map[string]interface{}) {
		propType, ok := schema["type"].(string)
		if !ok || propType != "object" {
			return
		}
		schema["maxProperties"] = max
	}
}

// MinProperties sets the minimum number of properties for an object
func MinProperties(min int) PropertyOption {
	return func(schema map[string]interface{}) {
		propType, ok := schema["type"].(string)
		if !ok || propType != "object" {
			return
		}
		schema["minProperties"] = min
	}
}

// Required sets the tool parameter as required.
// When a parameter is marked as required, the client must provide a value
// for this parameter or the tool call will fail with an error.
func Required() PropertyOption {
	return func(schema map[string]interface{}) {
		schema["required"] = true
	}
}

// Description sets the description for the tool parameter.
// The description should explain the purpose of the parameter, expected format,
// and any relevant constraints.
func Description(desc string) PropertyOption {
	return func(schema map[string]interface{}) {
		schema["description"] = desc
	}
}

// ToolParameter represents a parameter for a tool
type ToolParameter struct {
	Name   string
	Schema map[string]interface{}
}

// applyPropertyOptions applies the given property options to
// the parameter schema
func (p *ToolParameter) applyPropertyOptions(opts ...PropertyOption) {
	for _, opt := range opts {
		opt(p.Schema)
	}
}

// WithString creates a string parameter with optional property options
func WithString(name string, opts ...PropertyOption) ToolParameter {
	param := ToolParameter{
		Name:   name,
		Schema: map[string]interface{}{"type": "string"},
	}
	param.applyPropertyOptions(opts...)
	return param
}

// WithNumber creates a number parameter with optional property options
func WithNumber(name string, opts ...PropertyOption) ToolParameter {
	param := ToolParameter{
		Name:   name,
		Schema: map[string]interface{}{"type": "number"},
	}
	param.applyPropertyOptions(opts...)
	return param
}

// WithBoolean creates a boolean parameter with optional property options
func WithBoolean(name string, opts ...PropertyOption) ToolParameter {
	param := ToolParameter{
		Name:   name,
		Schema: map[string]interface{}{"type": "boolean"},
	}
	param.applyPropertyOptions(opts...)
	return param
}

// WithObject creates an object parameter with optional property options
func WithObject(name string, opts ...PropertyOption) ToolParameter {
	param := ToolParameter{
		Name:   name,
		Schema: map[string]interface{}{"type": "object"},
	}
	param.applyPropertyOptions(opts...)
	return param
}

// WithArray creates an array parameter with optional property options
func WithArray(name string, opts ...PropertyOption) ToolParameter {
	param := ToolParameter{
		Name:   name,
		Schema: map[string]interface{}{"type": "array"},
	}
	param.applyPropertyOptions(opts...)
	return param
}

// mark3labsToolImpl implements the Tool interface
type mark3labsToolImpl struct {
	name        string
	description string
	handler     ToolHandler
	parameters  []ToolParameter
}

// NewTool creates a new tool with the given
// Name, description, parameters and handler
func NewTool(
	name,
	description string,
	parameters []ToolParameter,
	handler ToolHandler) *mark3labsToolImpl {
	return &mark3labsToolImpl{
		name:        name,
		description: description,
		handler:     handler,
		parameters:  parameters,
	}
}

// addNumberPropertyOptions adds number-specific options to the property options
func addNumberPropertyOptions(
	propOpts []mcp.PropertyOption,
	schema map[string]interface{}) []mcp.PropertyOption {
	// Add minimum if present
	if min, ok := schema["minimum"].(float64); ok {
		propOpts = append(propOpts, mcp.Min(min))
	}

	// Add maximum if present
	if max, ok := schema["maximum"].(float64); ok {
		propOpts = append(propOpts, mcp.Max(max))
	}

	return propOpts
}

// addStringPropertyOptions adds string-specific options to the property options
func addStringPropertyOptions(
	propOpts []mcp.PropertyOption,
	schema map[string]interface{}) []mcp.PropertyOption {
	// Add minLength if present
	if minLength, ok := schema["minLength"].(int); ok {
		propOpts = append(propOpts, mcp.MinLength(minLength))
	}

	// Add maxLength if present
	if maxLength, ok := schema["maxLength"].(int); ok {
		propOpts = append(propOpts, mcp.MaxLength(maxLength))
	}

	// Add pattern if present
	if pattern, ok := schema["pattern"].(string); ok {
		propOpts = append(propOpts, mcp.Pattern(pattern))
	}

	return propOpts
}

// addDefaultValueOptions adds default value options based on type
func addDefaultValueOptions(
	propOpts []mcp.PropertyOption,
	defaultValue interface{}) []mcp.PropertyOption {
	switch val := defaultValue.(type) {
	case string:
		propOpts = append(propOpts, mcp.DefaultString(val))
	case float64:
		propOpts = append(propOpts, mcp.DefaultNumber(val))
	case bool:
		propOpts = append(propOpts, mcp.DefaultBool(val))
	}
	return propOpts
}

// addEnumOptions adds enum options if present
func addEnumOptions(
	propOpts []mcp.PropertyOption,
	enumValues interface{}) []mcp.PropertyOption {
	values, ok := enumValues.([]interface{})
	if !ok {
		return propOpts
	}

	// Convert values to strings for now
	strValues := make([]string, 0, len(values))
	for _, ev := range values {
		if str, ok := ev.(string); ok {
			strValues = append(strValues, str)
		}
	}

	if len(strValues) > 0 {
		propOpts = append(propOpts, mcp.Enum(strValues...))
	}

	return propOpts
}

// addObjectPropertyOptions adds object-specific options
func addObjectPropertyOptions(
	propOpts []mcp.PropertyOption,
	schema map[string]interface{}) []mcp.PropertyOption {
	// Add maxProperties if present
	if maxProps, ok := schema["maxProperties"].(int); ok {
		propOpts = append(propOpts, mcp.MaxProperties(maxProps))
	}

	// Add minProperties if present
	if minProps, ok := schema["minProperties"].(int); ok {
		propOpts = append(propOpts, mcp.MinProperties(minProps))
	}

	return propOpts
}

// addArrayPropertyOptions adds array-specific options
func addArrayPropertyOptions(
	propOpts []mcp.PropertyOption,
	schema map[string]interface{}) []mcp.PropertyOption {
	// Add minItems if present
	if minItems, ok := schema["minItems"].(int); ok {
		propOpts = append(propOpts, mcp.MinItems(minItems))
	}

	// Add maxItems if present
	if maxItems, ok := schema["maxItems"].(int); ok {
		propOpts = append(propOpts, mcp.MaxItems(maxItems))
	}

	return propOpts
}

// convertSchemaToPropertyOptions converts our schema to mcp property options
func convertSchemaToPropertyOptions(
	schema map[string]interface{}) []mcp.PropertyOption {
	var propOpts []mcp.PropertyOption

	// Add description if present
	if description, ok := schema["description"].(string); ok && description != "" {
		propOpts = append(propOpts, mcp.Description(description))
	}

	// Add required flag if present
	if required, ok := schema["required"].(bool); ok && required {
		propOpts = append(propOpts, mcp.Required())
	}

	// Skip type, description and required as they're handled separately
	for k, v := range schema {
		if k == "type" || k == "description" || k == "required" {
			continue
		}

		// Process property based on key
		switch k {
		case "minimum", "maximum":
			propOpts = addNumberPropertyOptions(propOpts, schema)
		case "minLength", "maxLength", "pattern":
			propOpts = addStringPropertyOptions(propOpts, schema)
		case "default":
			propOpts = addDefaultValueOptions(propOpts, v)
		case "enum":
			propOpts = addEnumOptions(propOpts, v)
		case "maxProperties", "minProperties":
			propOpts = addObjectPropertyOptions(propOpts, schema)
		case "minItems", "maxItems":
			propOpts = addArrayPropertyOptions(propOpts, schema)
		}
	}

	return propOpts
}

// GetHandler returns the handler for the tool
func (t *mark3labsToolImpl) GetHandler() ToolHandler {
	return t.handler
}

// toMCPServerTool converts our Tool to mcp's ServerTool
func (t *mark3labsToolImpl) toMCPServerTool() server.ServerTool {
	// Create the mcp tool with appropriate options
	var toolOpts []mcp.ToolOption

	// Add description
	toolOpts = append(toolOpts, mcp.WithDescription(t.description))

	// Add parameters with their schemas
	for _, param := range t.parameters {
		// Get property options from schema
		propOpts := convertSchemaToPropertyOptions(param.Schema)

		// Get the type from the schema
		schemaType, ok := param.Schema["type"].(string)
		if !ok {
			// Default to string if type is missing or not a string
			schemaType = "string"
		}

		// Use the appropriate function based on schema type
		switch schemaType {
		case "string":
			toolOpts = append(toolOpts, mcp.WithString(param.Name, propOpts...))
		case "number", "integer":
			toolOpts = append(toolOpts, mcp.WithNumber(param.Name, propOpts...))
		case "boolean":
			toolOpts = append(toolOpts, mcp.WithBoolean(param.Name, propOpts...))
		case "object":
			toolOpts = append(toolOpts, mcp.WithObject(param.Name, propOpts...))
		case "array":
			toolOpts = append(toolOpts, mcp.WithArray(param.Name, propOpts...))
		default:
			// Unknown type, default to string
			toolOpts = append(toolOpts, mcp.WithString(param.Name, propOpts...))
		}
	}

	// Create the tool with all options
	tool := mcp.NewTool(t.name, toolOpts...)

	// Create the handler
	handlerFunc := func(
		ctx context.Context,
		req mcp.CallToolRequest,
	) (*mcp.CallToolResult, error) {
		// Convert mcp request to our request
		ourReq := CallToolRequest{
			Name:      req.Params.Name,
			Arguments: req.Params.Arguments,
		}

		// Call our handler
		result, err := t.handler(ctx, ourReq)
		if err != nil {
			return nil, err
		}

		// Convert our result to mcp result
		var mcpResult *mcp.CallToolResult
		if result.IsError {
			mcpResult = mcp.NewToolResultError(result.Text)
		} else {
			mcpResult = mcp.NewToolResultText(result.Text)
		}

		return mcpResult, nil
	}

	return server.ServerTool{
		Tool:    tool,
		Handler: handlerFunc,
	}
}

// NewToolResultJSON creates a new tool result with JSON content
func NewToolResultJSON(data interface{}) (*ToolResult, error) {
	jsonBytes, err := json.Marshal(data)
	if err != nil {
		return nil, err
	}

	return &ToolResult{
		Text:    string(jsonBytes),
		IsError: false,
		Content: nil,
	}, nil
}

// NewToolResultText creates a new tool result with text content
func NewToolResultText(text string) *ToolResult {
	return &ToolResult{
		Text:    text,
		IsError: false,
		Content: nil,
	}
}

// NewToolResultError creates a new tool result with an error
func NewToolResultError(text string) *ToolResult {
	return &ToolResult{
		Text:    text,
		IsError: true,
		Content: nil,
	}
}

```

--------------------------------------------------------------------------------
/pkg/razorpay/tokens_test.go:
--------------------------------------------------------------------------------

```go
package razorpay

import (
	"context"
	"fmt"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"

	"github.com/razorpay/razorpay-go/constants"

	"github.com/razorpay/razorpay-mcp-server/pkg/contextkey"
	"github.com/razorpay/razorpay-mcp-server/pkg/mcpgo"
	"github.com/razorpay/razorpay-mcp-server/pkg/razorpay/mock"
)

func Test_FetchSavedPaymentMethods(t *testing.T) {
	// URL patterns for mocking
	createCustomerPath := fmt.Sprintf(
		"/%s%s",
		constants.VERSION_V1,
		constants.CUSTOMER_URL,
	)

	fetchTokensPathFmt := fmt.Sprintf(
		"/%s/customers/%%s/tokens",
		constants.VERSION_V1,
	)

	// Sample successful customer creation/fetch response
	customerResp := map[string]interface{}{
		"id":         "cust_1Aa00000000003",
		"entity":     "customer",
		"name":       "",
		"email":      "",
		"contact":    "9876543210",
		"gstin":      nil,
		"notes":      map[string]interface{}{},
		"created_at": float64(1234567890),
	}

	// Sample successful tokens response
	tokensResp := map[string]interface{}{
		"entity": "collection",
		"count":  float64(2),
		"items": []interface{}{
			map[string]interface{}{
				"id":     "token_ABCDEFGH",
				"entity": "token",
				"token":  "EhYXHrLsJdwRhM",
				"bank":   nil,
				"wallet": nil,
				"method": "card",
				"card": map[string]interface{}{
					"entity":        "card",
					"name":          "Gaurav Kumar",
					"last4":         "1111",
					"network":       "Visa",
					"type":          "debit",
					"issuer":        "HDFC",
					"international": false,
					"emi":           false,
					"sub_type":      "consumer",
				},
				"vpa":       nil,
				"recurring": true,
				"recurring_details": map[string]interface{}{
					"status":         "confirmed",
					"failure_reason": nil,
				},
				"auth_type":   nil,
				"mrn":         nil,
				"used_at":     float64(1629779657),
				"created_at":  float64(1629779657),
				"expired_at":  float64(1640918400),
				"dcc_enabled": false,
			},
			map[string]interface{}{
				"id":     "token_EhYXHrLsJdwRhN",
				"entity": "token",
				"token":  "EhYXHrLsJdwRhN",
				"bank":   nil,
				"wallet": nil,
				"method": "upi",
				"card":   nil,
				"vpa": map[string]interface{}{
					"username": "gauravkumar",
					"handle":   "okhdfcbank",
					"name":     "Gaurav Kumar",
				},
				"recurring": true,
				"recurring_details": map[string]interface{}{
					"status":         "confirmed",
					"failure_reason": nil,
				},
				"auth_type":   nil,
				"mrn":         nil,
				"used_at":     float64(1629779657),
				"created_at":  float64(1629779657),
				"expired_at":  float64(1640918400),
				"dcc_enabled": false,
			},
		},
	}

	// Expected combined response
	expectedSuccessResp := map[string]interface{}{
		"customer":              customerResp,
		"saved_payment_methods": tokensResp,
	}

	// Error responses
	customerCreationFailedResp := map[string]interface{}{
		"error": map[string]interface{}{
			"code":        "BAD_REQUEST_ERROR",
			"description": "Contact number is invalid",
		},
	}

	tokensAPIFailedResp := map[string]interface{}{
		"error": map[string]interface{}{
			"code":        "BAD_REQUEST_ERROR",
			"description": "Customer not found",
		},
	}

	// Customer response without ID (invalid)
	invalidCustomerResp := map[string]interface{}{
		"entity":     "customer",
		"name":       "",
		"email":      "",
		"contact":    "9876543210",
		"gstin":      nil,
		"notes":      map[string]interface{}{},
		"created_at": float64(1234567890),
		// Missing "id" field
	}

	tests := []RazorpayToolTestCase{
		{
			Name: "successful fetch of saved cards with valid contact",
			Request: map[string]interface{}{
				"contact": "9876543210",
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     createCustomerPath,
						Method:   "POST",
						Response: customerResp,
					},
					mock.Endpoint{
						Path:     fmt.Sprintf(fetchTokensPathFmt, "cust_1Aa00000000003"),
						Method:   "GET",
						Response: tokensResp,
					},
				)
			},
			ExpectError:    false,
			ExpectedResult: expectedSuccessResp,
		},
		{
			Name: "successful fetch with international contact format",
			Request: map[string]interface{}{
				"contact": "+919876543210",
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				customerRespIntl := map[string]interface{}{
					"id":         "cust_1Aa00000000004",
					"entity":     "customer",
					"name":       "",
					"email":      "",
					"contact":    "+919876543210",
					"gstin":      nil,
					"notes":      map[string]interface{}{},
					"created_at": float64(1234567890),
				}
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     createCustomerPath,
						Method:   "POST",
						Response: customerRespIntl,
					},
					mock.Endpoint{
						Path:     fmt.Sprintf(fetchTokensPathFmt, "cust_1Aa00000000004"),
						Method:   "GET",
						Response: tokensResp,
					},
				)
			},
			ExpectError: false,
			ExpectedResult: map[string]interface{}{
				"customer": map[string]interface{}{
					"id":         "cust_1Aa00000000004",
					"entity":     "customer",
					"name":       "",
					"email":      "",
					"contact":    "+919876543210",
					"gstin":      nil,
					"notes":      map[string]interface{}{},
					"created_at": float64(1234567890),
				},
				"saved_payment_methods": tokensResp,
			},
		},
		{
			Name: "customer creation/fetch failure",
			Request: map[string]interface{}{
				"contact": "invalid_contact",
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     createCustomerPath,
						Method:   "POST",
						Response: customerCreationFailedResp,
					},
				)
			},
			ExpectError: true,
			ExpectedErrMsg: "Failed to create/fetch customer with " +
				"contact invalid_contact: Contact number is invalid",
		},
		{
			Name: "tokens API failure after successful customer creation",
			Request: map[string]interface{}{
				"contact": "9876543210",
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     createCustomerPath,
						Method:   "POST",
						Response: customerResp,
					},
					mock.Endpoint{
						Path:     fmt.Sprintf(fetchTokensPathFmt, "cust_1Aa00000000003"),
						Method:   "GET",
						Response: tokensAPIFailedResp,
					},
				)
			},
			ExpectError: true,
			ExpectedErrMsg: "Failed to fetch saved payment methods for " +
				"customer cust_1Aa00000000003: Customer not found",
		},
		{
			Name: "invalid customer response - missing customer ID",
			Request: map[string]interface{}{
				"contact": "9876543210",
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     createCustomerPath,
						Method:   "POST",
						Response: invalidCustomerResp,
					},
				)
			},
			ExpectError:    true,
			ExpectedErrMsg: "Customer ID not found in response",
		},
		{
			Name:    "missing contact parameter",
			Request: map[string]interface{}{
				// No contact parameter
			},
			MockHttpClient: nil, // No HTTP client needed for validation error
			ExpectError:    true,
			ExpectedErrMsg: "missing required parameter: contact",
		},
		{
			Name: "empty contact parameter",
			Request: map[string]interface{}{
				"contact": "",
			},
			MockHttpClient: nil, // No HTTP client needed for validation error
			ExpectError:    true,
			ExpectedErrMsg: "missing required parameter: contact",
		},
		{
			Name: "null contact parameter",
			Request: map[string]interface{}{
				"contact": nil,
			},
			MockHttpClient: nil, // No HTTP client needed for validation error
			ExpectError:    true,
			ExpectedErrMsg: "missing required parameter: contact",
		},
		{
			Name: "successful fetch with empty tokens list",
			Request: map[string]interface{}{
				"contact": "9876543210",
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				emptyTokensResp := map[string]interface{}{
					"entity": "collection",
					"count":  float64(0),
					"items":  []interface{}{},
				}
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     createCustomerPath,
						Method:   "POST",
						Response: customerResp,
					},
					mock.Endpoint{
						Path:     fmt.Sprintf(fetchTokensPathFmt, "cust_1Aa00000000003"),
						Method:   "GET",
						Response: emptyTokensResp,
					},
				)
			},
			ExpectError: false,
			ExpectedResult: map[string]interface{}{
				"customer": customerResp,
				"saved_payment_methods": map[string]interface{}{
					"entity": "collection",
					"count":  float64(0),
					"items":  []interface{}{},
				},
			},
		},
	}

	for _, tc := range tests {
		t.Run(tc.Name, func(t *testing.T) {
			runToolTest(t, tc, FetchSavedPaymentMethods, "Saved Cards")
		})
	}
}

// Test_FetchSavedPaymentMethods_ClientContextScenarios tests scenarios
// related to client context handling for 100% code coverage
func Test_FetchSavedPaymentMethods_ClientContextScenarios(t *testing.T) {
	obs := CreateTestObservability()

	t.Run("no client in context and default is nil", func(t *testing.T) {
		// Create tool with nil client
		tool := FetchSavedPaymentMethods(obs, nil)

		// Create context without client
		ctx := context.Background()
		request := mcpgo.CallToolRequest{
			Arguments: map[string]interface{}{
				"contact": "9876543210",
			},
		}

		result, err := tool.GetHandler()(ctx, request)

		if err != nil {
			t.Fatalf("Expected no error, got %v", err)
		}

		if result == nil {
			t.Fatal("Expected result, got nil")
		}

		if result.Text == "" {
			t.Fatal("Expected error message in result")
		}

		expectedErrMsg := "no client found in context"
		if !strings.Contains(result.Text, expectedErrMsg) {
			t.Errorf(
				"Expected error message to contain '%s', got '%s'",
				expectedErrMsg,
				result.Text,
			)
		}
	})

	t.Run("invalid client type in context", func(t *testing.T) {
		// Create tool with nil client
		tool := FetchSavedPaymentMethods(obs, nil)

		// Create context with invalid client type
		ctx := contextkey.WithClient(context.Background(), "invalid_client_type")
		request := mcpgo.CallToolRequest{
			Arguments: map[string]interface{}{
				"contact": "9876543210",
			},
		}

		result, err := tool.GetHandler()(ctx, request)

		if err != nil {
			t.Fatalf("Expected no error, got %v", err)
		}

		if result == nil {
			t.Fatal("Expected result, got nil")
		}

		if result.Text == "" {
			t.Fatal("Expected error message in result")
		}

		expectedErrMsg := "invalid client type in context"
		if !strings.Contains(result.Text, expectedErrMsg) {
			t.Errorf(
				"Expected error message to contain '%s', got '%s'",
				expectedErrMsg,
				result.Text,
			)
		}
	})
}

func Test_RevokeToken(t *testing.T) {
	// URL patterns for mocking
	revokeTokenPathFmt := fmt.Sprintf(
		"/%s/customers/%%s/tokens/%%s/cancel",
		constants.VERSION_V1,
	)

	// Sample successful token revocation response
	successResp := map[string]interface{}{
		"deleted": true,
	}

	// Error responses
	tokenNotFoundResp := map[string]interface{}{
		"error": map[string]interface{}{
			"code":        "BAD_REQUEST_ERROR",
			"description": "Token not found",
		},
	}

	customerNotFoundResp := map[string]interface{}{
		"error": map[string]interface{}{
			"code":        "BAD_REQUEST_ERROR",
			"description": "Customer not found",
		},
	}

	tests := []RazorpayToolTestCase{
		{
			Name: "successful token revocation with valid parameters",
			Request: map[string]interface{}{
				"customer_id": "cust_1Aa00000000003",
				"token_id":    "token_ABCDEFGH",
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path: fmt.Sprintf(
							revokeTokenPathFmt,
							"cust_1Aa00000000003",
							"token_ABCDEFGH",
						),
						Method:   "PUT",
						Response: successResp,
					},
				)
			},
			ExpectError:    false,
			ExpectedResult: successResp,
		},
		{
			Name: "token not found error",
			Request: map[string]interface{}{
				"customer_id": "cust_1Aa00000000003",
				"token_id":    "token_nonexistent",
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path: fmt.Sprintf(
							revokeTokenPathFmt,
							"cust_1Aa00000000003",
							"token_nonexistent",
						),
						Method:   "PUT",
						Response: tokenNotFoundResp,
					},
				)
			},
			ExpectError: true,
			ExpectedErrMsg: "Failed to revoke token token_nonexistent for " +
				"customer cust_1Aa00000000003: Token not found",
		},
		{
			Name: "customer not found error",
			Request: map[string]interface{}{
				"customer_id": "cust_nonexistent",
				"token_id":    "token_ABCDEFGH",
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path: fmt.Sprintf(
							revokeTokenPathFmt,
							"cust_nonexistent",
							"token_ABCDEFGH",
						),
						Method:   "PUT",
						Response: customerNotFoundResp,
					},
				)
			},
			ExpectError: true,
			ExpectedErrMsg: "Failed to revoke token token_ABCDEFGH for " +
				"customer cust_nonexistent: Customer not found",
		},
		{
			Name: "missing customer_id parameter",
			Request: map[string]interface{}{
				"token_id": "token_ABCDEFGH",
			},
			MockHttpClient: nil, // No HTTP client needed for validation error
			ExpectError:    true,
			ExpectedErrMsg: "missing required parameter: customer_id",
		},
		{
			Name: "missing token_id parameter",
			Request: map[string]interface{}{
				"customer_id": "cust_1Aa00000000003",
			},
			MockHttpClient: nil, // No HTTP client needed for validation error
			ExpectError:    true,
			ExpectedErrMsg: "missing required parameter: token_id",
		},
		{
			Name: "empty customer_id parameter",
			Request: map[string]interface{}{
				"customer_id": "",
				"token_id":    "token_ABCDEFGH",
			},
			MockHttpClient: nil, // No HTTP client needed for validation error
			ExpectError:    true,
			ExpectedErrMsg: "missing required parameter: customer_id",
		},
		{
			Name: "empty token_id parameter",
			Request: map[string]interface{}{
				"customer_id": "cust_1Aa00000000003",
				"token_id":    "",
			},
			MockHttpClient: nil, // No HTTP client needed for validation error
			ExpectError:    true,
			ExpectedErrMsg: "missing required parameter: token_id",
		},
		{
			Name: "null customer_id parameter",
			Request: map[string]interface{}{
				"customer_id": nil,
				"token_id":    "token_ABCDEFGH",
			},
			MockHttpClient: nil, // No HTTP client needed for validation error
			ExpectError:    true,
			ExpectedErrMsg: "missing required parameter: customer_id",
		},
		{
			Name: "null token_id parameter",
			Request: map[string]interface{}{
				"customer_id": "cust_1Aa00000000003",
				"token_id":    nil,
			},
			MockHttpClient: nil, // No HTTP client needed for validation error
			ExpectError:    true,
			ExpectedErrMsg: "missing required parameter: token_id",
		},
		{
			Name:    "both parameters missing",
			Request: map[string]interface{}{
				// No parameters
			},
			MockHttpClient: nil, // No HTTP client needed for validation error
			ExpectError:    true,
			ExpectedErrMsg: "missing required parameter: customer_id",
		},
	}

	for _, tc := range tests {
		t.Run(tc.Name, func(t *testing.T) {
			runToolTest(t, tc, RevokeToken, "Revoke Token")
		})
	}
}

// Test_RevokeToken_ClientContextScenarios tests scenarios
// related to client context handling for 100% code coverage
func Test_RevokeToken_ClientContextScenarios(t *testing.T) {
	obs := CreateTestObservability()

	t.Run("no client in context and default is nil", func(t *testing.T) {
		// Create tool with nil client
		tool := RevokeToken(obs, nil)

		// Create context without client
		ctx := context.Background()
		request := mcpgo.CallToolRequest{
			Arguments: map[string]interface{}{
				"customer_id": "cust_1Aa00000000003",
				"token_id":    "token_ABCDEFGH",
			},
		}

		result, err := tool.GetHandler()(ctx, request)

		if err != nil {
			t.Fatalf("Expected no error, got %v", err)
		}

		if result == nil {
			t.Fatal("Expected result, got nil")
		}

		if result.Text == "" {
			t.Fatal("Expected error message in result")
		}

		expectedErrMsg := "no client found in context"
		if !strings.Contains(result.Text, expectedErrMsg) {
			t.Errorf(
				"Expected error message to contain '%s', got '%s'",
				expectedErrMsg,
				result.Text,
			)
		}
	})

	t.Run("invalid client type in context", func(t *testing.T) {
		// Create tool with nil client
		tool := RevokeToken(obs, nil)

		// Create context with invalid client type
		ctx := contextkey.WithClient(context.Background(), "invalid_client_type")
		request := mcpgo.CallToolRequest{
			Arguments: map[string]interface{}{
				"customer_id": "cust_1Aa00000000003",
				"token_id":    "token_ABCDEFGH",
			},
		}

		result, err := tool.GetHandler()(ctx, request)

		if err != nil {
			t.Fatalf("Expected no error, got %v", err)
		}

		if result == nil {
			t.Fatal("Expected result, got nil")
		}

		if result.Text == "" {
			t.Fatal("Expected error message in result")
		}

		expectedErrMsg := "invalid client type in context"
		if !strings.Contains(result.Text, expectedErrMsg) {
			t.Errorf(
				"Expected error message to contain '%s', got '%s'",
				expectedErrMsg,
				result.Text,
			)
		}
	})
}

```

--------------------------------------------------------------------------------
/pkg/razorpay/payment_links_test.go:
--------------------------------------------------------------------------------

```go
package razorpay

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/razorpay/razorpay-go/constants"

	"github.com/razorpay/razorpay-mcp-server/pkg/razorpay/mock"
)

func Test_CreatePaymentLink(t *testing.T) {
	createPaymentLinkPath := fmt.Sprintf(
		"/%s%s",
		constants.VERSION_V1,
		constants.PaymentLink_URL,
	)

	successfulPaymentLinkResp := map[string]interface{}{
		"id":          "plink_ExjpAUN3gVHrPJ",
		"amount":      float64(50000),
		"currency":    "INR",
		"description": "Test payment",
		"status":      "created",
		"short_url":   "https://rzp.io/i/nxrHnLJ",
	}

	paymentLinkWithoutDescResp := map[string]interface{}{
		"id":        "plink_ExjpAUN3gVHrPJ",
		"amount":    float64(50000),
		"currency":  "INR",
		"status":    "created",
		"short_url": "https://rzp.io/i/nxrHnLJ",
	}

	invalidCurrencyErrorResp := map[string]interface{}{
		"error": map[string]interface{}{
			"code":        "BAD_REQUEST_ERROR",
			"description": "API error: Invalid currency",
		},
	}

	tests := []RazorpayToolTestCase{
		{
			Name: "successful payment link creation",
			Request: map[string]interface{}{
				"amount":      float64(50000),
				"currency":    "INR",
				"description": "Test payment",
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     createPaymentLinkPath,
						Method:   "POST",
						Response: successfulPaymentLinkResp,
					},
				)
			},
			ExpectError:    false,
			ExpectedResult: successfulPaymentLinkResp,
		},
		{
			Name: "payment link without description",
			Request: map[string]interface{}{
				"amount":   float64(50000),
				"currency": "INR",
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     createPaymentLinkPath,
						Method:   "POST",
						Response: paymentLinkWithoutDescResp,
					},
				)
			},
			ExpectError:    false,
			ExpectedResult: paymentLinkWithoutDescResp,
		},
		{
			Name: "missing amount parameter",
			Request: map[string]interface{}{
				"currency": "INR",
			},
			MockHttpClient: nil, // No HTTP client needed for validation error
			ExpectError:    true,
			ExpectedErrMsg: "missing required parameter: amount",
		},
		{
			Name: "missing currency parameter",
			Request: map[string]interface{}{
				"amount": float64(50000),
			},
			MockHttpClient: nil, // No HTTP client needed for validation error
			ExpectError:    true,
			ExpectedErrMsg: "missing required parameter: currency",
		},
		{
			Name: "multiple validation errors",
			Request: map[string]interface{}{
				// Missing both amount and currency (required parameters)
				"description": 12345, // Wrong type for description
			},
			MockHttpClient: nil, // No HTTP client needed for validation error
			ExpectError:    true,
			ExpectedErrMsg: "Validation errors:\n- " +
				"missing required parameter: amount\n- " +
				"missing required parameter: currency\n- " +
				"invalid parameter type: description",
		},
		{
			Name: "payment link creation fails",
			Request: map[string]interface{}{
				"amount":   float64(50000),
				"currency": "XYZ", // Invalid currency
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     createPaymentLinkPath,
						Method:   "POST",
						Response: invalidCurrencyErrorResp,
					},
				)
			},
			ExpectError:    true,
			ExpectedErrMsg: "creating payment link failed: API error: Invalid currency",
		},
	}

	for _, tc := range tests {
		t.Run(tc.Name, func(t *testing.T) {
			runToolTest(t, tc, CreatePaymentLink, "Payment Link")
		})
	}
}

func Test_FetchPaymentLink(t *testing.T) {
	fetchPaymentLinkPathFmt := fmt.Sprintf(
		"/%s%s/%%s",
		constants.VERSION_V1,
		constants.PaymentLink_URL,
	)

	// Define common response maps to be reused
	paymentLinkResp := map[string]interface{}{
		"id":          "plink_ExjpAUN3gVHrPJ",
		"amount":      float64(50000),
		"currency":    "INR",
		"description": "Test payment",
		"status":      "paid",
		"short_url":   "https://rzp.io/i/nxrHnLJ",
	}

	paymentLinkNotFoundResp := map[string]interface{}{
		"error": map[string]interface{}{
			"code":        "BAD_REQUEST_ERROR",
			"description": "payment link not found",
		},
	}

	tests := []RazorpayToolTestCase{
		{
			Name: "successful payment link fetch",
			Request: map[string]interface{}{
				"payment_link_id": "plink_ExjpAUN3gVHrPJ",
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     fmt.Sprintf(fetchPaymentLinkPathFmt, "plink_ExjpAUN3gVHrPJ"),
						Method:   "GET",
						Response: paymentLinkResp,
					},
				)
			},
			ExpectError:    false,
			ExpectedResult: paymentLinkResp,
		},
		{
			Name: "payment link not found",
			Request: map[string]interface{}{
				"payment_link_id": "plink_invalid",
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     fmt.Sprintf(fetchPaymentLinkPathFmt, "plink_invalid"),
						Method:   "GET",
						Response: paymentLinkNotFoundResp,
					},
				)
			},
			ExpectError:    true,
			ExpectedErrMsg: "fetching payment link failed: payment link not found",
		},
		{
			Name:           "missing payment_link_id parameter",
			Request:        map[string]interface{}{},
			MockHttpClient: nil, // No HTTP client needed for validation error
			ExpectError:    true,
			ExpectedErrMsg: "missing required parameter: payment_link_id",
		},
		{
			Name: "multiple validation errors",
			Request: map[string]interface{}{
				// Missing payment_link_id parameter
				"non_existent_param": 12345, // Additional parameter that doesn't exist
			},
			MockHttpClient: nil, // No HTTP client needed for validation error
			ExpectError:    true,
			ExpectedErrMsg: "missing required parameter: payment_link_id",
		},
	}

	for _, tc := range tests {
		t.Run(tc.Name, func(t *testing.T) {
			runToolTest(t, tc, FetchPaymentLink, "Payment Link")
		})
	}
}

func Test_CreateUpiPaymentLink(t *testing.T) {
	createPaymentLinkPath := fmt.Sprintf(
		"/%s%s",
		constants.VERSION_V1,
		constants.PaymentLink_URL,
	)

	upiPaymentLinkWithAllParamsResp := map[string]interface{}{
		"id":              "plink_UpiAllParamsExjpAUN3gVHrPJ",
		"amount":          float64(50000),
		"currency":        "INR",
		"description":     "Test UPI payment with all params",
		"reference_id":    "REF12345",
		"accept_partial":  true,
		"expire_by":       float64(1718196584),
		"reminder_enable": true,
		"status":          "created",
		"short_url":       "https://rzp.io/i/upiAllParams123",
		"upi_link":        true,
		"customer": map[string]interface{}{
			"name":    "Test Customer",
			"email":   "[email protected]",
			"contact": "+919876543210",
		},
		"notes": map[string]interface{}{
			"policy_name": "Test Policy",
			"user_id":     "usr_123",
		},
	}

	errorResp := map[string]interface{}{
		"error": map[string]interface{}{
			"code":        "BAD_REQUEST_ERROR",
			"description": "API error: Something went wrong",
		},
	}

	tests := []RazorpayToolTestCase{
		{
			Name: "UPI payment link with all parameters",
			Request: map[string]interface{}{
				"amount":                   float64(50000),
				"currency":                 "INR",
				"description":              "Test UPI payment with all params",
				"reference_id":             "REF12345",
				"accept_partial":           true,
				"first_min_partial_amount": float64(10000),
				"expire_by":                float64(1718196584),
				"customer_name":            "Test Customer",
				"customer_email":           "[email protected]",
				"customer_contact":         "+919876543210",
				"notify_sms":               true,
				"notify_email":             true,
				"reminder_enable":          true,
				"notes": map[string]interface{}{
					"policy_name": "Test Policy",
					"user_id":     "usr_123",
				},
				"callback_url":    "https://example.com/callback",
				"callback_method": "get",
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     createPaymentLinkPath,
						Method:   "POST",
						Response: upiPaymentLinkWithAllParamsResp,
					},
				)
			},
			ExpectError:    false,
			ExpectedResult: upiPaymentLinkWithAllParamsResp,
		},
		{
			Name:           "missing amount parameter",
			Request:        map[string]interface{}{},
			MockHttpClient: nil, // No HTTP client needed for validation error
			ExpectError:    true,
			ExpectedErrMsg: "missing required parameter: amount",
		},
		{
			Name: "UPI payment link creation fails",
			Request: map[string]interface{}{
				"amount": float64(50000),
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     createPaymentLinkPath,
						Method:   "POST",
						Response: errorResp,
					},
				)
			},
			ExpectError:    true,
			ExpectedErrMsg: "missing required parameter: currency",
		},
	}

	for _, tc := range tests {
		t.Run(tc.Name, func(t *testing.T) {
			runToolTest(t, tc, CreateUpiPaymentLink, "UPI Payment Link")
		})
	}
}

func Test_ResendPaymentLinkNotification(t *testing.T) {
	notifyPaymentLinkPathFmt := fmt.Sprintf(
		"/%s%s/%%s/notify_by/%%s",
		constants.VERSION_V1,
		constants.PaymentLink_URL,
	)

	successResponse := map[string]interface{}{
		"success": true,
	}

	invalidMediumErrorResp := map[string]interface{}{
		"error": map[string]interface{}{
			"code":        "BAD_REQUEST_ERROR",
			"description": "not a valid notification medium",
		},
	}

	tests := []RazorpayToolTestCase{
		{
			Name: "successful SMS notification",
			Request: map[string]interface{}{
				"payment_link_id": "plink_ExjpAUN3gVHrPJ",
				"medium":          "sms",
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path: fmt.Sprintf(
							notifyPaymentLinkPathFmt,
							"plink_ExjpAUN3gVHrPJ",
							"sms",
						),
						Method:   "POST",
						Response: successResponse,
					},
				)
			},
			ExpectError:    false,
			ExpectedResult: successResponse,
		},
		{
			Name: "missing payment_link_id parameter",
			Request: map[string]interface{}{
				"medium": "sms",
			},
			MockHttpClient: nil, // No HTTP client needed for validation error
			ExpectError:    true,
			ExpectedErrMsg: "missing required parameter: payment_link_id",
		},
		{
			Name: "missing medium parameter",
			Request: map[string]interface{}{
				"payment_link_id": "plink_ExjpAUN3gVHrPJ",
			},
			MockHttpClient: nil, // No HTTP client needed for validation error
			ExpectError:    true,
			ExpectedErrMsg: "missing required parameter: medium",
		},
		{
			Name: "API error response",
			Request: map[string]interface{}{
				"payment_link_id": "plink_Invalid",
				"medium":          "sms", // Using valid medium so it passes validation
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path: fmt.Sprintf(
							notifyPaymentLinkPathFmt,
							"plink_Invalid",
							"sms",
						),
						Method:   "POST",
						Response: invalidMediumErrorResp,
					},
				)
			},
			ExpectError: true,
			ExpectedErrMsg: "sending notification failed: " +
				"not a valid notification medium",
		},
	}

	for _, tc := range tests {
		t.Run(tc.Name, func(t *testing.T) {
			toolFunc := ResendPaymentLinkNotification
			runToolTest(t, tc, toolFunc, "Payment Link Notification")
		})
	}
}

func Test_UpdatePaymentLink(t *testing.T) {
	updatePaymentLinkPathFmt := fmt.Sprintf(
		"/%s%s/%%s",
		constants.VERSION_V1,
		constants.PaymentLink_URL,
	)

	updatedPaymentLinkResp := map[string]interface{}{
		"id":              "plink_FL5HCrWEO112OW",
		"amount":          float64(1000),
		"currency":        "INR",
		"status":          "created",
		"reference_id":    "TS35",
		"expire_by":       float64(1612092283),
		"reminder_enable": false,
		"notes": []interface{}{
			map[string]interface{}{
				"key":   "policy_name",
				"value": "Jeevan Saral",
			},
		},
	}

	invalidStateResp := map[string]interface{}{
		"error": map[string]interface{}{
			"code":        "BAD_REQUEST_ERROR",
			"description": "update can only be made in created or partially paid state",
		},
	}

	tests := []RazorpayToolTestCase{
		{
			Name: "successful update with multiple fields",
			Request: map[string]interface{}{
				"payment_link_id": "plink_FL5HCrWEO112OW",
				"reference_id":    "TS35",
				"expire_by":       float64(1612092283),
				"reminder_enable": false,
				"accept_partial":  true,
				"notes": map[string]interface{}{
					"policy_name": "Jeevan Saral",
				},
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path: fmt.Sprintf(
							updatePaymentLinkPathFmt,
							"plink_FL5HCrWEO112OW",
						),
						Method:   "PATCH",
						Response: updatedPaymentLinkResp,
					},
				)
			},
			ExpectError:    false,
			ExpectedResult: updatedPaymentLinkResp,
		},
		{
			Name: "successful update with single field",
			Request: map[string]interface{}{
				"payment_link_id": "plink_FL5HCrWEO112OW",
				"reference_id":    "TS35",
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path: fmt.Sprintf(
							updatePaymentLinkPathFmt,
							"plink_FL5HCrWEO112OW",
						),
						Method:   "PATCH",
						Response: updatedPaymentLinkResp,
					},
				)
			},
			ExpectError:    false,
			ExpectedResult: updatedPaymentLinkResp,
		},
		{
			Name: "missing payment_link_id parameter",
			Request: map[string]interface{}{
				"reference_id": "TS35",
			},
			MockHttpClient: nil, // No HTTP client needed for validation error
			ExpectError:    true,
			ExpectedErrMsg: "missing required parameter: payment_link_id",
		},
		{
			Name: "no update fields provided",
			Request: map[string]interface{}{
				"payment_link_id": "plink_FL5HCrWEO112OW",
			},
			MockHttpClient: nil, // No HTTP client needed for validation error
			ExpectError:    true,
			ExpectedErrMsg: "at least one field to update must be provided",
		},
		{
			Name: "payment link in invalid state",
			Request: map[string]interface{}{
				"payment_link_id": "plink_Paid",
				"reference_id":    "TS35",
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path: fmt.Sprintf(
							updatePaymentLinkPathFmt,
							"plink_Paid",
						),
						Method:   "PATCH",
						Response: invalidStateResp,
					},
				)
			},
			ExpectError: true,
			ExpectedErrMsg: "updating payment link failed: update can only be made in " +
				"created or partially paid state",
		},
		{
			Name: "update with explicit false value",
			Request: map[string]interface{}{
				"payment_link_id": "plink_FL5HCrWEO112OW",
				"reminder_enable": false, // Explicitly set to false
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path: fmt.Sprintf(
							updatePaymentLinkPathFmt,
							"plink_FL5HCrWEO112OW",
						),
						Method:   "PATCH",
						Response: updatedPaymentLinkResp,
					},
				)
			},
			ExpectError:    false,
			ExpectedResult: updatedPaymentLinkResp,
		},
	}

	for _, tc := range tests {
		t.Run(tc.Name, func(t *testing.T) {
			toolFunc := UpdatePaymentLink
			runToolTest(t, tc, toolFunc, "Payment Link Update")
		})
	}
}

func Test_FetchAllPaymentLinks(t *testing.T) {
	fetchAllPaymentLinksPath := fmt.Sprintf(
		"/%s%s",
		constants.VERSION_V1,
		constants.PaymentLink_URL,
	)

	allPaymentLinksResp := map[string]interface{}{
		"payment_links": []interface{}{
			map[string]interface{}{
				"id":           "plink_KBnb7I424Rc1R9",
				"amount":       float64(10000),
				"currency":     "INR",
				"status":       "paid",
				"description":  "Grocery",
				"reference_id": "111",
				"short_url":    "https://rzp.io/i/alaBxs0i",
				"upi_link":     false,
			},
			map[string]interface{}{
				"id":           "plink_JP6yOUDCuHgcrl",
				"amount":       float64(10000),
				"currency":     "INR",
				"status":       "paid",
				"description":  "Online Tutoring - 1 Month",
				"reference_id": "11212",
				"short_url":    "https://rzp.io/i/0ioYuawFu",
				"upi_link":     false,
			},
		},
	}

	errorResp := map[string]interface{}{
		"error": map[string]interface{}{
			"code":        "BAD_REQUEST_ERROR",
			"description": "The api key/secret provided is invalid",
		},
	}

	tests := []RazorpayToolTestCase{
		{
			Name:    "fetch all payment links",
			Request: map[string]interface{}{},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     fetchAllPaymentLinksPath,
						Method:   "GET",
						Response: allPaymentLinksResp,
					},
				)
			},
			ExpectError:    false,
			ExpectedResult: allPaymentLinksResp,
		},
		{
			Name:    "api error",
			Request: map[string]interface{}{},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     fetchAllPaymentLinksPath,
						Method:   "GET",
						Response: errorResp,
					},
				)
			},
			ExpectError:    true,
			ExpectedErrMsg: "fetching payment links failed: The api key/secret provided is invalid", // nolint:lll
		},
	}

	for _, tc := range tests {
		t.Run(tc.Name, func(t *testing.T) {
			toolFunc := FetchAllPaymentLinks
			runToolTest(t, tc, toolFunc, "Payment Links")
		})
	}
}

```

--------------------------------------------------------------------------------
/pkg/razorpay/payment_links.go:
--------------------------------------------------------------------------------

```go
package razorpay

import (
	"context"
	"fmt"

	rzpsdk "github.com/razorpay/razorpay-go"

	"github.com/razorpay/razorpay-mcp-server/pkg/mcpgo"
	"github.com/razorpay/razorpay-mcp-server/pkg/observability"
)

// CreatePaymentLink returns a tool that creates payment links in Razorpay
func CreatePaymentLink(
	obs *observability.Observability,
	client *rzpsdk.Client,
) mcpgo.Tool {
	parameters := []mcpgo.ToolParameter{
		mcpgo.WithNumber(
			"amount",
			mcpgo.Description("Amount to be paid using the link in smallest "+
				"currency unit(e.g., ₹300, use 30000)"),
			mcpgo.Required(),
			mcpgo.Min(100), // Minimum amount is 100 (1.00 in currency)
		),
		mcpgo.WithString(
			"currency",
			mcpgo.Description("Three-letter ISO code for the currency (e.g., INR)"),
			mcpgo.Required(),
		),
		mcpgo.WithString(
			"description",
			mcpgo.Description("A brief description of the Payment Link explaining the intent of the payment."), // nolint:lll
		),
		mcpgo.WithBoolean(
			"accept_partial",
			mcpgo.Description("Indicates whether customers can make partial payments using the Payment Link. Default: false"), // nolint:lll
		),
		mcpgo.WithNumber(
			"first_min_partial_amount",
			mcpgo.Description("Minimum amount that must be paid by the customer as the first partial payment. Default value is 100."), // nolint:lll
		),
		mcpgo.WithNumber(
			"expire_by",
			mcpgo.Description("Timestamp, in Unix, when the Payment Link will expire. By default, a Payment Link will be valid for six months."), // nolint:lll
		),
		mcpgo.WithString(
			"reference_id",
			mcpgo.Description("Reference number tagged to a Payment Link. Must be unique for each Payment Link. Max 40 characters."), // nolint:lll
		),
		mcpgo.WithString(
			"customer_name",
			mcpgo.Description("Name of the customer."),
		),
		mcpgo.WithString(
			"customer_email",
			mcpgo.Description("Email address of the customer."),
		),
		mcpgo.WithString(
			"customer_contact",
			mcpgo.Description("Contact number of the customer."),
		),
		mcpgo.WithBoolean(
			"notify_sms",
			mcpgo.Description("Send SMS notifications for the Payment Link."),
		),
		mcpgo.WithBoolean(
			"notify_email",
			mcpgo.Description("Send email notifications for the Payment Link."),
		),
		mcpgo.WithBoolean(
			"reminder_enable",
			mcpgo.Description("Enable payment reminders for the Payment Link."),
		),
		mcpgo.WithObject(
			"notes",
			mcpgo.Description("Key-value pairs that can be used to store additional information. Maximum 15 pairs, each value limited to 256 characters."), // nolint:lll
		),
		mcpgo.WithString(
			"callback_url",
			mcpgo.Description("If specified, adds a redirect URL to the Payment Link. Customer will be redirected here after payment."), // nolint:lll
		),
		mcpgo.WithString(
			"callback_method",
			mcpgo.Description("HTTP method for callback redirection. "+
				"Must be 'get' if callback_url is set."),
		),
	}

	handler := func(
		ctx context.Context,
		r mcpgo.CallToolRequest,
	) (*mcpgo.ToolResult, error) {
		// Get client from context or use default
		client, err := getClientFromContextOrDefault(ctx, client)
		if err != nil {
			return mcpgo.NewToolResultError(err.Error()), nil
		}

		// Create a parameters map to collect validated parameters
		plCreateReq := make(map[string]interface{})
		customer := make(map[string]interface{})
		notify := make(map[string]interface{})
		// Validate all parameters with fluent validator
		validator := NewValidator(&r).
			ValidateAndAddRequiredInt(plCreateReq, "amount").
			ValidateAndAddRequiredString(plCreateReq, "currency").
			ValidateAndAddOptionalString(plCreateReq, "description").
			ValidateAndAddOptionalBool(plCreateReq, "accept_partial").
			ValidateAndAddOptionalInt(plCreateReq, "first_min_partial_amount").
			ValidateAndAddOptionalInt(plCreateReq, "expire_by").
			ValidateAndAddOptionalString(plCreateReq, "reference_id").
			ValidateAndAddOptionalStringToPath(customer, "customer_name", "name").
			ValidateAndAddOptionalStringToPath(customer, "customer_email", "email").
			ValidateAndAddOptionalStringToPath(customer, "customer_contact", "contact").
			ValidateAndAddOptionalBoolToPath(notify, "notify_sms", "sms").
			ValidateAndAddOptionalBoolToPath(notify, "notify_email", "email").
			ValidateAndAddOptionalBool(plCreateReq, "reminder_enable").
			ValidateAndAddOptionalMap(plCreateReq, "notes").
			ValidateAndAddOptionalString(plCreateReq, "callback_url").
			ValidateAndAddOptionalString(plCreateReq, "callback_method")

		if result, err := validator.HandleErrorsIfAny(); result != nil {
			return result, err
		}

		// Handle customer details
		if len(customer) > 0 {
			plCreateReq["customer"] = customer
		}

		// Handle notification settings
		if len(notify) > 0 {
			plCreateReq["notify"] = notify
		}

		// Create the payment link
		paymentLink, err := client.PaymentLink.Create(plCreateReq, nil)
		if err != nil {
			return mcpgo.NewToolResultError(
				fmt.Sprintf("creating payment link failed: %s", err.Error())), nil
		}

		return mcpgo.NewToolResultJSON(paymentLink)
	}

	return mcpgo.NewTool(
		"create_payment_link",
		"Create a new standard payment link in Razorpay with a specified amount",
		parameters,
		handler,
	)
}

// CreateUpiPaymentLink returns a tool that creates payment links in Razorpay
func CreateUpiPaymentLink(
	obs *observability.Observability,
	client *rzpsdk.Client,
) mcpgo.Tool {
	parameters := []mcpgo.ToolParameter{
		mcpgo.WithNumber(
			"amount",
			mcpgo.Description("Amount to be paid using the link in smallest currency unit(e.g., ₹300, use 30000), Only accepted currency is INR"), // nolint:lll
			mcpgo.Required(),
			mcpgo.Min(100), // Minimum amount is 100 (1.00 in currency)
		),
		mcpgo.WithString(
			"currency",
			mcpgo.Description("Three-letter ISO code for the currency (e.g., INR). UPI links are only supported in INR"), // nolint:lll
			mcpgo.Required(),
		),
		mcpgo.WithString(
			"description",
			mcpgo.Description("A brief description of the Payment Link explaining the intent of the payment."), // nolint:lll
		),
		mcpgo.WithBoolean(
			"accept_partial",
			mcpgo.Description("Indicates whether customers can make partial payments using the Payment Link. Default: false"), // nolint:lll
		),
		mcpgo.WithNumber(
			"first_min_partial_amount",
			mcpgo.Description("Minimum amount that must be paid by the customer as the first partial payment. Default value is 100."), // nolint:lll
		),
		mcpgo.WithNumber(
			"expire_by",
			mcpgo.Description("Timestamp, in Unix, when the Payment Link will expire. By default, a Payment Link will be valid for six months."), // nolint:lll
		),
		mcpgo.WithString(
			"reference_id",
			mcpgo.Description("Reference number tagged to a Payment Link. Must be unique for each Payment Link. Max 40 characters."), // nolint:lll
		),
		mcpgo.WithString(
			"customer_name",
			mcpgo.Description("Name of the customer."),
		),
		mcpgo.WithString(
			"customer_email",
			mcpgo.Description("Email address of the customer."),
		),
		mcpgo.WithString(
			"customer_contact",
			mcpgo.Description("Contact number of the customer."),
		),
		mcpgo.WithBoolean(
			"notify_sms",
			mcpgo.Description("Send SMS notifications for the Payment Link."),
		),
		mcpgo.WithBoolean(
			"notify_email",
			mcpgo.Description("Send email notifications for the Payment Link."),
		),
		mcpgo.WithBoolean(
			"reminder_enable",
			mcpgo.Description("Enable payment reminders for the Payment Link."),
		),
		mcpgo.WithObject(
			"notes",
			mcpgo.Description("Key-value pairs that can be used to store additional information. Maximum 15 pairs, each value limited to 256 characters."), // nolint:lll
		),
		mcpgo.WithString(
			"callback_url",
			mcpgo.Description("If specified, adds a redirect URL to the Payment Link. Customer will be redirected here after payment."), // nolint:lll
		),
		mcpgo.WithString(
			"callback_method",
			mcpgo.Description("HTTP method for callback redirection. "+
				"Must be 'get' if callback_url is set."),
		),
	}

	handler := func(
		ctx context.Context,
		r mcpgo.CallToolRequest,
	) (*mcpgo.ToolResult, error) {
		// Create a parameters map to collect validated parameters
		upiPlCreateReq := make(map[string]interface{})
		customer := make(map[string]interface{})
		notify := make(map[string]interface{})
		// Validate all parameters with fluent validator
		validator := NewValidator(&r).
			ValidateAndAddRequiredInt(upiPlCreateReq, "amount").
			ValidateAndAddRequiredString(upiPlCreateReq, "currency").
			ValidateAndAddOptionalString(upiPlCreateReq, "description").
			ValidateAndAddOptionalBool(upiPlCreateReq, "accept_partial").
			ValidateAndAddOptionalInt(upiPlCreateReq, "first_min_partial_amount").
			ValidateAndAddOptionalInt(upiPlCreateReq, "expire_by").
			ValidateAndAddOptionalString(upiPlCreateReq, "reference_id").
			ValidateAndAddOptionalStringToPath(customer, "customer_name", "name").
			ValidateAndAddOptionalStringToPath(customer, "customer_email", "email").
			ValidateAndAddOptionalStringToPath(customer, "customer_contact", "contact").
			ValidateAndAddOptionalBoolToPath(notify, "notify_sms", "sms").
			ValidateAndAddOptionalBoolToPath(notify, "notify_email", "email").
			ValidateAndAddOptionalBool(upiPlCreateReq, "reminder_enable").
			ValidateAndAddOptionalMap(upiPlCreateReq, "notes").
			ValidateAndAddOptionalString(upiPlCreateReq, "callback_url").
			ValidateAndAddOptionalString(upiPlCreateReq, "callback_method")

		if result, err := validator.HandleErrorsIfAny(); result != nil {
			return result, err
		}

		// Add the required UPI payment link parameters
		upiPlCreateReq["upi_link"] = "true"

		// Handle customer details
		if len(customer) > 0 {
			upiPlCreateReq["customer"] = customer
		}

		// Handle notification settings
		if len(notify) > 0 {
			upiPlCreateReq["notify"] = notify
		}

		client, err := getClientFromContextOrDefault(ctx, client)
		if err != nil {
			return mcpgo.NewToolResultError(err.Error()), nil
		}

		// Create the payment link
		paymentLink, err := client.PaymentLink.Create(upiPlCreateReq, nil)
		if err != nil {
			return mcpgo.NewToolResultError(
				fmt.Sprintf("upi pl create failed: %s", err.Error())), nil
		}

		return mcpgo.NewToolResultJSON(paymentLink)
	}

	return mcpgo.NewTool(
		"payment_link_upi_create",
		"Create a new UPI payment link in Razorpay with a specified amount and additional options.", // nolint:lll
		parameters,
		handler,
	)
}

// FetchPaymentLink returns a tool that fetches payment link details using
// payment_link_id
func FetchPaymentLink(
	obs *observability.Observability,
	client *rzpsdk.Client,
) mcpgo.Tool {
	parameters := []mcpgo.ToolParameter{
		mcpgo.WithString(
			"payment_link_id",
			mcpgo.Description("ID of the payment link to be fetched"+
				"(ID should have a plink_ prefix)."),
			mcpgo.Required(),
		),
	}

	handler := func(
		ctx context.Context,
		r mcpgo.CallToolRequest,
	) (*mcpgo.ToolResult, error) {
		// Get client from context or use default
		client, err := getClientFromContextOrDefault(ctx, client)
		if err != nil {
			return mcpgo.NewToolResultError(err.Error()), nil
		}

		fields := make(map[string]interface{})

		validator := NewValidator(&r).
			ValidateAndAddRequiredString(fields, "payment_link_id")

		if result, err := validator.HandleErrorsIfAny(); result != nil {
			return result, err
		}

		paymentLinkId := fields["payment_link_id"].(string)

		paymentLink, err := client.PaymentLink.Fetch(paymentLinkId, nil, nil)
		if err != nil {
			return mcpgo.NewToolResultError(
				fmt.Sprintf("fetching payment link failed: %s", err.Error())), nil
		}

		return mcpgo.NewToolResultJSON(paymentLink)
	}

	return mcpgo.NewTool(
		"fetch_payment_link",
		"Fetch payment link details using it's ID. "+
			"Response contains the basic details like amount, status etc. "+
			"The link could be of any type(standard or UPI)",
		parameters,
		handler,
	)
}

// ResendPaymentLinkNotification returns a tool that sends/resends notifications
// for a payment link via email or SMS
func ResendPaymentLinkNotification(
	obs *observability.Observability,
	client *rzpsdk.Client,
) mcpgo.Tool {
	parameters := []mcpgo.ToolParameter{
		mcpgo.WithString(
			"payment_link_id",
			mcpgo.Description("ID of the payment link for which to send notification "+
				"(ID should have a plink_ prefix)."), // nolint:lll
			mcpgo.Required(),
		),
		mcpgo.WithString(
			"medium",
			mcpgo.Description("Medium through which to send the notification. "+
				"Must be either 'sms' or 'email'."), // nolint:lll
			mcpgo.Required(),
			mcpgo.Enum("sms", "email"),
		),
	}

	handler := func(
		ctx context.Context,
		r mcpgo.CallToolRequest,
	) (*mcpgo.ToolResult, error) {
		client, err := getClientFromContextOrDefault(ctx, client)
		if err != nil {
			return mcpgo.NewToolResultError(err.Error()), nil
		}

		fields := make(map[string]interface{})

		validator := NewValidator(&r).
			ValidateAndAddRequiredString(fields, "payment_link_id").
			ValidateAndAddRequiredString(fields, "medium")

		if result, err := validator.HandleErrorsIfAny(); result != nil {
			return result, err
		}

		paymentLinkId := fields["payment_link_id"].(string)
		medium := fields["medium"].(string)

		// Call the SDK function
		response, err := client.PaymentLink.NotifyBy(paymentLinkId, medium, nil, nil)
		if err != nil {
			return mcpgo.NewToolResultError(
				fmt.Sprintf("sending notification failed: %s", err.Error())), nil
		}

		return mcpgo.NewToolResultJSON(response)
	}

	return mcpgo.NewTool(
		"payment_link_notify",
		"Send or resend notification for a payment link via SMS or email.", // nolint:lll
		parameters,
		handler,
	)
}

// UpdatePaymentLink returns a tool that updates an existing payment link
func UpdatePaymentLink(
	obs *observability.Observability,
	client *rzpsdk.Client,
) mcpgo.Tool {
	parameters := []mcpgo.ToolParameter{
		mcpgo.WithString(
			"payment_link_id",
			mcpgo.Description("ID of the payment link to update "+
				"(ID should have a plink_ prefix)."),
			mcpgo.Required(),
		),
		mcpgo.WithString(
			"reference_id",
			mcpgo.Description("Adds a unique reference number to the payment link."),
		),
		mcpgo.WithNumber(
			"expire_by",
			mcpgo.Description("Timestamp, in Unix format, when the payment link "+
				"should expire."),
		),
		mcpgo.WithBoolean(
			"reminder_enable",
			mcpgo.Description("Enable or disable reminders for the payment link."),
		),
		mcpgo.WithBoolean(
			"accept_partial",
			mcpgo.Description("Allow customers to make partial payments. "+
				"Not allowed with UPI payment links."),
		),
		mcpgo.WithObject(
			"notes",
			mcpgo.Description("Key-value pairs for additional information. "+
				"Maximum 15 pairs, each value limited to 256 characters."),
		),
	}

	handler := func(
		ctx context.Context,
		r mcpgo.CallToolRequest,
	) (*mcpgo.ToolResult, error) {
		client, err := getClientFromContextOrDefault(ctx, client)
		if err != nil {
			return mcpgo.NewToolResultError(err.Error()), nil
		}

		plUpdateReq := make(map[string]interface{})
		otherFields := make(map[string]interface{})

		validator := NewValidator(&r).
			ValidateAndAddRequiredString(otherFields, "payment_link_id").
			ValidateAndAddOptionalString(plUpdateReq, "reference_id").
			ValidateAndAddOptionalInt(plUpdateReq, "expire_by").
			ValidateAndAddOptionalBool(plUpdateReq, "reminder_enable").
			ValidateAndAddOptionalBool(plUpdateReq, "accept_partial").
			ValidateAndAddOptionalMap(plUpdateReq, "notes")

		if result, err := validator.HandleErrorsIfAny(); result != nil {
			return result, err
		}

		paymentLinkId := otherFields["payment_link_id"].(string)

		// Ensure we have at least one field to update
		if len(plUpdateReq) == 0 {
			return mcpgo.NewToolResultError(
				"at least one field to update must be provided"), nil
		}

		// Call the SDK function
		paymentLink, err := client.PaymentLink.Update(paymentLinkId, plUpdateReq, nil)
		if err != nil {
			return mcpgo.NewToolResultError(
				fmt.Sprintf("updating payment link failed: %s", err.Error())), nil
		}

		return mcpgo.NewToolResultJSON(paymentLink)
	}

	return mcpgo.NewTool(
		"update_payment_link",
		"Update any existing standard or UPI payment link with new details such as reference ID, "+ // nolint:lll
			"expiry date, or notes.",
		parameters,
		handler,
	)
}

// FetchAllPaymentLinks returns a tool that fetches all payment links
// with optional filtering
func FetchAllPaymentLinks(
	obs *observability.Observability,
	client *rzpsdk.Client,
) mcpgo.Tool {
	parameters := []mcpgo.ToolParameter{
		mcpgo.WithString(
			"payment_id",
			mcpgo.Description("Optional: Filter by payment ID associated with payment links"), // nolint:lll
		),
		mcpgo.WithString(
			"reference_id",
			mcpgo.Description("Optional: Filter by reference ID used when creating payment links"), // nolint:lll
		),
		mcpgo.WithNumber(
			"upi_link",
			mcpgo.Description("Optional: Filter only upi links. "+
				"Value should be 1 if you want only upi links, 0 for only standard links"+
				"If not provided, all types of links will be returned"),
		),
	}

	handler := func(
		ctx context.Context,
		r mcpgo.CallToolRequest,
	) (*mcpgo.ToolResult, error) {
		client, err := getClientFromContextOrDefault(ctx, client)
		if err != nil {
			return mcpgo.NewToolResultError(err.Error()), nil
		}

		plListReq := make(map[string]interface{})

		validator := NewValidator(&r).
			ValidateAndAddOptionalString(plListReq, "payment_id").
			ValidateAndAddOptionalString(plListReq, "reference_id").
			ValidateAndAddOptionalInt(plListReq, "upi_link")

		if result, err := validator.HandleErrorsIfAny(); result != nil {
			return result, err
		}

		// Call the API directly using the Request object
		response, err := client.PaymentLink.All(plListReq, nil)
		if err != nil {
			return mcpgo.NewToolResultError(
				fmt.Sprintf("fetching payment links failed: %s", err.Error())), nil
		}

		return mcpgo.NewToolResultJSON(response)
	}

	return mcpgo.NewTool(
		"fetch_all_payment_links",
		"Fetch all payment links with optional filtering by payment ID or reference ID."+ // nolint:lll
			"You can specify the upi_link parameter to filter by link type.",
		parameters,
		handler,
	)
}

```

--------------------------------------------------------------------------------
/pkg/razorpay/refunds_test.go:
--------------------------------------------------------------------------------

```go
package razorpay

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/razorpay/razorpay-go/constants"

	"github.com/razorpay/razorpay-mcp-server/pkg/razorpay/mock"
)

func Test_CreateRefund(t *testing.T) {
	createRefundPathFmt := fmt.Sprintf(
		"/%s%s/%%s/refund",
		constants.VERSION_V1,
		constants.PAYMENT_URL,
	)

	// Define test responses
	successfulRefundResp := map[string]interface{}{
		"id":              "rfnd_FP8QHiV938haTz",
		"entity":          "refund",
		"amount":          float64(500100),
		"currency":        "INR",
		"payment_id":      "pay_29QQoUBi66xm2f",
		"notes":           map[string]interface{}{},
		"receipt":         "Receipt No. 31",
		"acquirer_data":   map[string]interface{}{"arn": nil},
		"created_at":      float64(1597078866),
		"batch_id":        nil,
		"status":          "processed",
		"speed_processed": "normal",
		"speed_requested": "normal",
	}

	errorResp := map[string]interface{}{
		"error": map[string]interface{}{
			"code":        "BAD_REQUEST_ERROR",
			"description": "Razorpay API error: Bad request",
		},
	}

	tests := []RazorpayToolTestCase{
		{
			Name: "successful full refund",
			Request: map[string]interface{}{
				"payment_id": "pay_29QQoUBi66xm2f",
				"amount":     float64(500100),
				"receipt":    "Receipt No. 31",
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     fmt.Sprintf(createRefundPathFmt, "pay_29QQoUBi66xm2f"),
						Method:   "POST",
						Response: successfulRefundResp,
					},
				)
			},
			ExpectError:    false,
			ExpectedResult: successfulRefundResp,
		},
		{
			Name: "refund with speed parameter",
			Request: map[string]interface{}{
				"payment_id": "pay_29QQoUBi66xm2f",
				"amount":     float64(500100),
				"speed":      "optimum",
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				speedRefundResp := map[string]interface{}{
					"id":              "rfnd_HzAbPEkKtRq48V",
					"entity":          "refund",
					"amount":          float64(500100),
					"payment_id":      "pay_29QQoUBi66xm2f",
					"status":          "processed",
					"speed_processed": "instant",
					"speed_requested": "optimum",
				}
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     fmt.Sprintf(createRefundPathFmt, "pay_29QQoUBi66xm2f"),
						Method:   "POST",
						Response: speedRefundResp,
					},
				)
			},
			ExpectError: false,
			ExpectedResult: map[string]interface{}{
				"id":              "rfnd_HzAbPEkKtRq48V",
				"entity":          "refund",
				"amount":          float64(500100),
				"payment_id":      "pay_29QQoUBi66xm2f",
				"status":          "processed",
				"speed_processed": "instant",
				"speed_requested": "optimum",
			},
		},
		{
			Name: "refund API server error",
			Request: map[string]interface{}{
				"payment_id": "pay_29QQoUBi66xm2f",
				"amount":     float64(500100),
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     fmt.Sprintf(createRefundPathFmt, "pay_29QQoUBi66xm2f"),
						Method:   "POST",
						Response: errorResp,
					},
				)
			},
			ExpectError:    true,
			ExpectedErrMsg: "creating refund failed: Razorpay API error: Bad request",
		},
		{
			Name: "multiple validation errors",
			Request: map[string]interface{}{
				// Missing payment_id parameter
				"amount": "not-a-number",  // Wrong type for amount
				"speed":  12345,           // Wrong type for speed
				"notes":  "not-an-object", // Wrong type for notes
			},
			MockHttpClient: nil,
			ExpectError:    true,
			ExpectedErrMsg: "Validation errors:\n- " +
				"missing required parameter: payment_id\n- " +
				"invalid parameter type: amount\n- " +
				"invalid parameter type: speed\n- " +
				"invalid parameter type: notes",
		},
	}

	for _, tc := range tests {
		t.Run(tc.Name, func(t *testing.T) {
			runToolTest(t, tc, CreateRefund, "Refund")
		})
	}
}

func Test_FetchRefund(t *testing.T) {
	fetchRefundPathFmt := fmt.Sprintf(
		"/%s%s/%%s",
		constants.VERSION_V1,
		constants.REFUND_URL,
	)

	// Define test response for successful refund fetch
	successfulRefundResp := map[string]interface{}{
		"id":         "rfnd_DfjjhJC6eDvUAi",
		"entity":     "refund",
		"amount":     float64(6000),
		"currency":   "INR",
		"payment_id": "pay_EpkFDYRirena0f",
		"notes": map[string]interface{}{
			"comment": "Issuing an instant refund",
		},
		"receipt": nil,
		"acquirer_data": map[string]interface{}{
			"arn": "10000000000000",
		},
		"created_at":      float64(1589521675),
		"batch_id":        nil,
		"status":          "processed",
		"speed_processed": "optimum",
		"speed_requested": "optimum",
	}

	// Define error responses
	notFoundResp := map[string]interface{}{
		"error": map[string]interface{}{
			"code":        "BAD_REQUEST_ERROR",
			"description": "The id provided does not exist",
		},
	}

	tests := []RazorpayToolTestCase{
		{
			Name: "successful refund fetch",
			Request: map[string]interface{}{
				"refund_id": "rfnd_DfjjhJC6eDvUAi",
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     fmt.Sprintf(fetchRefundPathFmt, "rfnd_DfjjhJC6eDvUAi"),
						Method:   "GET",
						Response: successfulRefundResp,
					},
				)
			},
			ExpectError:    false,
			ExpectedResult: successfulRefundResp,
		},
		{
			Name: "refund id not found",
			Request: map[string]interface{}{
				"refund_id": "rfnd_nonexistent",
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     fmt.Sprintf(fetchRefundPathFmt, "rfnd_nonexistent"),
						Method:   "GET",
						Response: notFoundResp,
					},
				)
			},
			ExpectError:    true,
			ExpectedErrMsg: "fetching refund failed: The id provided does not exist",
		},
		{
			Name:           "missing refund_id parameter",
			Request:        map[string]interface{}{},
			MockHttpClient: nil,
			ExpectError:    true,
			ExpectedErrMsg: "missing required parameter: refund_id",
		},
		{
			Name: "multiple validation errors",
			Request: map[string]interface{}{
				// Missing refund_id parameter
				"non_existent_param": 12345, // Additional parameter that doesn't exist
			},
			MockHttpClient: nil,
			ExpectError:    true,
			ExpectedErrMsg: "missing required parameter: refund_id",
		},
	}

	for _, tc := range tests {
		t.Run(tc.Name, func(t *testing.T) {
			runToolTest(t, tc, FetchRefund, "Refund")
		})
	}
}

func Test_UpdateRefund(t *testing.T) {
	updateRefundPathFmt := fmt.Sprintf(
		"/%s%s/%%s",
		constants.VERSION_V1,
		constants.REFUND_URL,
	)

	// Define test response for successful refund update
	successfulUpdateResp := map[string]interface{}{
		"id":         "rfnd_DfjjhJC6eDvUAi",
		"entity":     "refund",
		"amount":     float64(300100),
		"currency":   "INR",
		"payment_id": "pay_FIKOnlyii5QGNx",
		"notes": map[string]interface{}{
			"notes_key_1": "Beam me up Scotty.",
			"notes_key_2": "Engage",
		},
		"receipt":         nil,
		"acquirer_data":   map[string]interface{}{"arn": "10000000000000"},
		"created_at":      float64(1597078124),
		"batch_id":        nil,
		"status":          "processed",
		"speed_processed": "normal",
		"speed_requested": "optimum",
	}

	// Define error responses
	notFoundResp := map[string]interface{}{
		"error": map[string]interface{}{
			"code":        "BAD_REQUEST_ERROR",
			"description": "The id provided does not exist",
		},
	}

	tests := []RazorpayToolTestCase{
		{
			Name: "successful refund update",
			Request: map[string]interface{}{
				"refund_id": "rfnd_DfjjhJC6eDvUAi",
				"notes": map[string]interface{}{
					"notes_key_1": "Beam me up Scotty.",
					"notes_key_2": "Engage",
				},
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     fmt.Sprintf(updateRefundPathFmt, "rfnd_DfjjhJC6eDvUAi"),
						Method:   "PATCH",
						Response: successfulUpdateResp,
					},
				)
			},
			ExpectError:    false,
			ExpectedResult: successfulUpdateResp,
		},
		{
			Name: "refund id not found",
			Request: map[string]interface{}{
				"refund_id": "rfnd_nonexistent",
				"notes": map[string]interface{}{
					"note_key": "Test note",
				},
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     fmt.Sprintf(updateRefundPathFmt, "rfnd_nonexistent"),
						Method:   "PATCH",
						Response: notFoundResp,
					},
				)
			},
			ExpectError:    true,
			ExpectedErrMsg: "updating refund failed: The id provided does not exist",
		},
		{
			Name:           "missing refund_id parameter",
			Request:        map[string]interface{}{},
			MockHttpClient: nil,
			ExpectError:    true,
			ExpectedErrMsg: "missing required parameter: refund_id",
		},
		{
			Name: "missing notes parameter",
			Request: map[string]interface{}{
				"refund_id": "rfnd_DfjjhJC6eDvUAi",
			},
			MockHttpClient: nil,
			ExpectError:    true,
			ExpectedErrMsg: "missing required parameter: notes",
		},
		{
			Name: "multiple validation errors",
			Request: map[string]interface{}{
				// Missing both refund_id and notes parameters
				"non_existent_param": 12345, // Additional parameter that doesn't exist
			},
			MockHttpClient: nil,
			ExpectError:    true,
			ExpectedErrMsg: "Validation errors:\n- " +
				"missing required parameter: refund_id\n- " +
				"missing required parameter: notes",
		},
	}

	for _, tc := range tests {
		t.Run(tc.Name, func(t *testing.T) {
			runToolTest(t, tc, UpdateRefund, "Refund")
		})
	}
}

func Test_FetchMultipleRefundsForPayment(t *testing.T) {
	fetchMultipleRefundsPathFmt := fmt.Sprintf(
		"/%s%s/%%s/refunds",
		constants.VERSION_V1,
		constants.PAYMENT_URL,
	)

	// Define test response for successful multiple refunds fetch
	successfulMultipleRefundsResp := map[string]interface{}{
		"entity": "collection",
		"count":  float64(2),
		"items": []interface{}{
			map[string]interface{}{
				"id":         "rfnd_FP8DDKxqJif6ca",
				"entity":     "refund",
				"amount":     float64(300100),
				"currency":   "INR",
				"payment_id": "pay_29QQoUBi66xm2f",
				"notes": map[string]interface{}{
					"comment": "Comment for refund",
				},
				"receipt": nil,
				"acquirer_data": map[string]interface{}{
					"arn": "10000000000000",
				},
				"created_at":      float64(1597078124),
				"batch_id":        nil,
				"status":          "processed",
				"speed_processed": "normal",
				"speed_requested": "optimum",
			},
			map[string]interface{}{
				"id":         "rfnd_FP8DRfu3ygfOaC",
				"entity":     "refund",
				"amount":     float64(200000),
				"currency":   "INR",
				"payment_id": "pay_29QQoUBi66xm2f",
				"notes": map[string]interface{}{
					"comment": "Comment for refund",
				},
				"receipt": nil,
				"acquirer_data": map[string]interface{}{
					"arn": "10000000000000",
				},
				"created_at":      float64(1597078137),
				"batch_id":        nil,
				"status":          "processed",
				"speed_processed": "normal",
				"speed_requested": "optimum",
			},
		},
	}

	// Define error responses
	errorResp := map[string]interface{}{
		"error": map[string]interface{}{
			"code":        "BAD_REQUEST_ERROR",
			"description": "Bad request",
		},
	}

	tests := []RazorpayToolTestCase{
		{
			Name: "fetch multiple refunds with query params",
			Request: map[string]interface{}{
				"payment_id": "pay_29QQoUBi66xm2f",
				"from":       1500826740,
				"to":         1500826760,
				"count":      10,
				"skip":       0,
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path: fmt.Sprintf(
							fetchMultipleRefundsPathFmt,
							"pay_29QQoUBi66xm2f",
						),
						Method:   "GET",
						Response: successfulMultipleRefundsResp,
					},
				)
			},
			ExpectError:    false,
			ExpectedResult: successfulMultipleRefundsResp,
		},
		{
			Name: "fetch multiple refunds api error",
			Request: map[string]interface{}{
				"payment_id": "pay_invalid",
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path: fmt.Sprintf(
							fetchMultipleRefundsPathFmt,
							"pay_invalid",
						),
						Method:   "GET",
						Response: errorResp,
					},
				)
			},
			ExpectError:    true,
			ExpectedErrMsg: "fetching multiple refunds failed: Bad request",
		},
		{
			Name:           "missing payment_id parameter",
			Request:        map[string]interface{}{},
			MockHttpClient: nil,
			ExpectError:    true,
			ExpectedErrMsg: "missing required parameter: payment_id",
		},
		{
			Name: "multiple validation errors",
			Request: map[string]interface{}{
				// Missing payment_id parameter
				"from":  "not-a-number", // Wrong type for from
				"to":    "not-a-number", // Wrong type for to
				"count": "not-a-number", // Wrong type for count
				"skip":  "not-a-number", // Wrong type for skip
			},
			MockHttpClient: nil,
			ExpectError:    true,
			ExpectedErrMsg: "Validation errors:\n- " +
				"missing required parameter: payment_id\n- " +
				"invalid parameter type: from\n- " +
				"invalid parameter type: to\n- " +
				"invalid parameter type: count\n- " +
				"invalid parameter type: skip",
		},
	}

	for _, tc := range tests {
		t.Run(tc.Name, func(t *testing.T) {
			runToolTest(t, tc, FetchMultipleRefundsForPayment, "Refund")
		})
	}
}

func Test_FetchSpecificRefundForPayment(t *testing.T) {
	fetchSpecificRefundPathFmt := fmt.Sprintf(
		"/%s%s/%%s/refunds/%%s",
		constants.VERSION_V1,
		constants.PAYMENT_URL,
	)

	// Define test response for successful specific refund fetch
	successfulSpecificRefundResp := map[string]interface{}{
		"id":         "rfnd_AABBdHIieexn5c",
		"entity":     "refund",
		"amount":     float64(300100),
		"currency":   "INR",
		"payment_id": "pay_FIKOnlyii5QGNx",
		"notes": map[string]interface{}{
			"comment": "Comment for refund",
		},
		"receipt":         nil,
		"acquirer_data":   map[string]interface{}{"arn": "10000000000000"},
		"created_at":      float64(1597078124),
		"batch_id":        nil,
		"status":          "processed",
		"speed_processed": "normal",
		"speed_requested": "optimum",
	}

	// Define error responses
	notFoundResp := map[string]interface{}{
		"error": map[string]interface{}{
			"code":        "BAD_REQUEST_ERROR",
			"description": "The id provided does not exist",
		},
	}

	tests := []RazorpayToolTestCase{
		{
			Name: "successful specific refund fetch",
			Request: map[string]interface{}{
				"payment_id": "pay_FIKOnlyii5QGNx",
				"refund_id":  "rfnd_AABBdHIieexn5c",
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path: fmt.Sprintf(
							fetchSpecificRefundPathFmt,
							"pay_FIKOnlyii5QGNx",
							"rfnd_AABBdHIieexn5c",
						),
						Method:   "GET",
						Response: successfulSpecificRefundResp,
					},
				)
			},
			ExpectError:    false,
			ExpectedResult: successfulSpecificRefundResp,
		},
		{
			Name: "refund id not found",
			Request: map[string]interface{}{
				"payment_id": "pay_FIKOnlyii5QGNx",
				"refund_id":  "rfnd_nonexistent",
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path: fmt.Sprintf(
							fetchSpecificRefundPathFmt,
							"pay_FIKOnlyii5QGNx",
							"rfnd_nonexistent",
						),
						Method:   "GET",
						Response: notFoundResp,
					},
				)
			},
			ExpectError: true,
			ExpectedErrMsg: "fetching specific refund for payment failed: " +
				"The id provided does not exist",
		},
		{
			Name: "missing payment_id parameter",
			Request: map[string]interface{}{
				"refund_id": "rfnd_AABBdHIieexn5c",
			},
			MockHttpClient: nil,
			ExpectError:    true,
			ExpectedErrMsg: "missing required parameter: payment_id",
		},
		{
			Name: "missing refund_id parameter",
			Request: map[string]interface{}{
				"payment_id": "pay_FIKOnlyii5QGNx",
			},
			MockHttpClient: nil,
			ExpectError:    true,
			ExpectedErrMsg: "missing required parameter: refund_id",
		},
		{
			Name: "multiple validation errors",
			Request: map[string]interface{}{
				// Missing both payment_id and refund_id parameters
				"non_existent_param": 12345, // Additional parameter that doesn't exist
			},
			MockHttpClient: nil,
			ExpectError:    true,
			ExpectedErrMsg: "Validation errors:\n- " +
				"missing required parameter: payment_id\n- " +
				"missing required parameter: refund_id",
		},
	}

	for _, tc := range tests {
		t.Run(tc.Name, func(t *testing.T) {
			runToolTest(t, tc, FetchSpecificRefundForPayment, "Refund")
		})
	}
}

func Test_FetchAllRefunds(t *testing.T) {
	fetchAllRefundsPath := fmt.Sprintf(
		"/%s%s",
		constants.VERSION_V1,
		constants.REFUND_URL,
	)

	// Define test response for successful refund fetch
	successfulRefundsResp := map[string]interface{}{
		"entity": "collection",
		"count":  float64(2),
		"items": []interface{}{
			map[string]interface{}{
				"id":         "rfnd_FFX6AnnIN3puqW",
				"entity":     "refund",
				"amount":     float64(88800),
				"currency":   "INR",
				"payment_id": "pay_FFX5FdEYx8jPwA",
				"notes": map[string]interface{}{
					"comment": "Issuing an instant refund",
				},
				"receipt":         nil,
				"acquirer_data":   map[string]interface{}{},
				"created_at":      float64(1594982363),
				"batch_id":        nil,
				"status":          "processed",
				"speed_processed": "optimum",
				"speed_requested": "optimum",
			},
			map[string]interface{}{
				"id":         "rfnd_EqWThTE7dd7utf",
				"entity":     "refund",
				"amount":     float64(6000),
				"currency":   "INR",
				"payment_id": "pay_EpkFDYRirena0f",
				"notes": map[string]interface{}{
					"comment": "Issuing a normal refund",
				},
				"receipt": nil,
				"acquirer_data": map[string]interface{}{
					"arn": "10000000000000",
				},
				"created_at":      float64(1589521675),
				"batch_id":        nil,
				"status":          "processed",
				"speed_processed": "normal",
				"speed_requested": "normal",
			},
		},
	}

	// Define error response
	errorResp := map[string]interface{}{
		"error": map[string]interface{}{
			"code":        "BAD_REQUEST_ERROR",
			"description": "Bad request",
		},
	}

	tests := []RazorpayToolTestCase{
		{
			Name: "successful fetch with pagination parameters",
			Request: map[string]interface{}{
				"count": 2,
				"skip":  1,
				"from":  1589000000,
				"to":    1595000000,
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     fetchAllRefundsPath,
						Method:   "GET",
						Response: successfulRefundsResp,
					},
				)
			},
			ExpectError:    false,
			ExpectedResult: successfulRefundsResp,
		},
		{
			Name:    "fetch with API error",
			Request: map[string]interface{}{},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     fetchAllRefundsPath,
						Method:   "GET",
						Response: errorResp,
					},
				)
			},
			ExpectError:    true,
			ExpectedErrMsg: "fetching refunds failed",
		},
		{
			Name: "multiple validation errors",
			Request: map[string]interface{}{
				"from":  "not-a-number", // Wrong type for from
				"to":    "not-a-number", // Wrong type for to
				"count": "not-a-number", // Wrong type for count
				"skip":  "not-a-number", // Wrong type for skip
			},
			MockHttpClient: nil,
			ExpectError:    true,
			ExpectedErrMsg: "Validation errors:\n- " +
				"invalid parameter type: from\n- " +
				"invalid parameter type: to\n- " +
				"invalid parameter type: count\n- " +
				"invalid parameter type: skip",
		},
	}

	for _, tc := range tests {
		t.Run(tc.Name, func(t *testing.T) {
			runToolTest(t, tc, FetchAllRefunds, "Refund")
		})
	}
}

```

--------------------------------------------------------------------------------
/pkg/razorpay/settlements_test.go:
--------------------------------------------------------------------------------

```go
package razorpay

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/razorpay/razorpay-go/constants"

	"github.com/razorpay/razorpay-mcp-server/pkg/razorpay/mock"
)

func Test_FetchSettlement(t *testing.T) {
	fetchSettlementPathFmt := fmt.Sprintf(
		"/%s%s/%%s",
		constants.VERSION_V1,
		constants.SETTLEMENT_URL,
	)

	settlementResp := map[string]interface{}{
		"id":         "setl_FNj7g2YS5J67Rz",
		"entity":     "settlement",
		"amount":     float64(9973635),
		"status":     "processed",
		"fees":       float64(471),
		"tax":        float64(72),
		"utr":        "1568176198",
		"created_at": float64(1568176198),
	}

	settlementNotFoundResp := map[string]interface{}{
		"error": map[string]interface{}{
			"code":        "BAD_REQUEST_ERROR",
			"description": "settlement not found",
		},
	}

	tests := []RazorpayToolTestCase{
		{
			Name: "successful settlement fetch",
			Request: map[string]interface{}{
				"settlement_id": "setl_FNj7g2YS5J67Rz",
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     fmt.Sprintf(fetchSettlementPathFmt, "setl_FNj7g2YS5J67Rz"),
						Method:   "GET",
						Response: settlementResp,
					},
				)
			},
			ExpectError:    false,
			ExpectedResult: settlementResp,
		},
		{
			Name: "settlement not found",
			Request: map[string]interface{}{
				"settlement_id": "setl_invalid",
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     fmt.Sprintf(fetchSettlementPathFmt, "setl_invalid"),
						Method:   "GET",
						Response: settlementNotFoundResp,
					},
				)
			},
			ExpectError:    true,
			ExpectedErrMsg: "fetching settlement failed: settlement not found",
		},
		{
			Name:           "missing settlement_id parameter",
			Request:        map[string]interface{}{},
			MockHttpClient: nil, // No HTTP client needed for validation error
			ExpectError:    true,
			ExpectedErrMsg: "missing required parameter: settlement_id",
		},
	}

	for _, tc := range tests {
		t.Run(tc.Name, func(t *testing.T) {
			runToolTest(t, tc, FetchSettlement, "Settlement")
		})
	}
}

func Test_FetchSettlementRecon(t *testing.T) {
	fetchSettlementReconPath := fmt.Sprintf(
		"/%s%s/recon/combined",
		constants.VERSION_V1,
		constants.SETTLEMENT_URL,
	)

	settlementReconResp := map[string]interface{}{
		"entity": "collection",
		"count":  float64(1),
		"items": []interface{}{
			map[string]interface{}{
				"entity":            "settlement",
				"settlement_id":     "setl_FNj7g2YS5J67Rz",
				"settlement_utr":    "1568176198",
				"amount":            float64(9973635),
				"settlement_type":   "regular",
				"settlement_status": "processed",
				"created_at":        float64(1568176198),
			},
		},
	}

	invalidParamsResp := map[string]interface{}{
		"error": map[string]interface{}{
			"code":        "BAD_REQUEST_ERROR",
			"description": "missing required parameters",
		},
	}

	tests := []RazorpayToolTestCase{
		{
			Name: "successful settlement reconciliation fetch",
			Request: map[string]interface{}{
				"year":  float64(2022),
				"month": float64(10),
				"day":   float64(15),
				"count": float64(10),
				"skip":  float64(0),
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     fetchSettlementReconPath,
						Method:   "GET",
						Response: settlementReconResp,
					},
				)
			},
			ExpectError:    false,
			ExpectedResult: settlementReconResp,
		},
		{
			Name: "settlement reconciliation with required params only",
			Request: map[string]interface{}{
				"year":  float64(2022),
				"month": float64(10),
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     fetchSettlementReconPath,
						Method:   "GET",
						Response: settlementReconResp,
					},
				)
			},
			ExpectError:    false,
			ExpectedResult: settlementReconResp,
		},
		{
			Name: "settlement reconciliation with invalid params",
			Request: map[string]interface{}{
				"year": float64(2022),
				// missing month parameter
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     fetchSettlementReconPath,
						Method:   "GET",
						Response: invalidParamsResp,
					},
				)
			},
			ExpectError:    true,
			ExpectedErrMsg: "missing required parameter: month",
		},
		{
			Name:           "missing required parameters",
			Request:        map[string]interface{}{},
			MockHttpClient: nil, // No HTTP client needed for validation error
			ExpectError:    true,
			ExpectedErrMsg: "missing required parameter: year",
		},
	}

	for _, tc := range tests {
		t.Run(tc.Name, func(t *testing.T) {
			runToolTest(t, tc, FetchSettlementRecon, "Settlement Reconciliation")
		})
	}
}

func Test_FetchAllSettlements(t *testing.T) {
	fetchAllSettlementsPath := fmt.Sprintf(
		"/%s%s",
		constants.VERSION_V1,
		constants.SETTLEMENT_URL,
	)

	// Define the sample response for all settlements
	settlementsResp := map[string]interface{}{
		"entity": "collection",
		"count":  float64(2),
		"items": []interface{}{
			map[string]interface{}{
				"id":     "setl_FNj7g2YS5J67Rz",
				"entity": "settlement",
				"amount": float64(9973635),
				"status": "processed",
			},
			map[string]interface{}{
				"id":     "setl_FJOp0jOWlalIvt",
				"entity": "settlement",
				"amount": float64(299114),
				"status": "processed",
			},
		},
	}

	invalidParamsResp := map[string]interface{}{
		"error": map[string]interface{}{
			"code":        "BAD_REQUEST_ERROR",
			"description": "from must be between 946684800 and 4765046400",
		},
	}

	tests := []RazorpayToolTestCase{
		{
			Name:    "successful settlements fetch with no parameters",
			Request: map[string]interface{}{},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     fetchAllSettlementsPath,
						Method:   "GET",
						Response: settlementsResp,
					},
				)
			},
			ExpectError:    false,
			ExpectedResult: settlementsResp,
		},
		{
			Name: "successful settlements fetch with pagination",
			Request: map[string]interface{}{
				"count": float64(10),
				"skip":  float64(0),
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     fetchAllSettlementsPath,
						Method:   "GET",
						Response: settlementsResp,
					},
				)
			},
			ExpectError:    false,
			ExpectedResult: settlementsResp,
		},
		{
			Name: "successful settlements fetch with date range",
			Request: map[string]interface{}{
				"from": float64(1609459200), // 2021-01-01
				"to":   float64(1640995199), // 2021-12-31
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     fetchAllSettlementsPath,
						Method:   "GET",
						Response: settlementsResp,
					},
				)
			},
			ExpectError:    false,
			ExpectedResult: settlementsResp,
		},
		{
			Name: "settlements fetch with invalid timestamp",
			Request: map[string]interface{}{
				"from": float64(900000000), // Invalid timestamp (too early)
				"to":   float64(1600000000),
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     fetchAllSettlementsPath,
						Method:   "GET",
						Response: invalidParamsResp,
					},
				)
			},
			ExpectError: true,
			ExpectedErrMsg: "fetching settlements failed: from must be " +
				"between 946684800 and 4765046400",
		},
	}

	for _, tc := range tests {
		t.Run(tc.Name, func(t *testing.T) {
			runToolTest(t, tc, FetchAllSettlements, "Settlements List")
		})
	}
}

func Test_CreateInstantSettlement(t *testing.T) {
	createInstantSettlementPath := fmt.Sprintf(
		"/%s%s/ondemand",
		constants.VERSION_V1,
		constants.SETTLEMENT_URL,
	)

	// Successful response with all parameters
	successfulSettlementResp := map[string]interface{}{
		"id":                  "setlod_FNj7g2YS5J67Rz",
		"entity":              "settlement.ondemand",
		"amount_requested":    float64(200000),
		"amount_settled":      float64(0),
		"amount_pending":      float64(199410),
		"amount_reversed":     float64(0),
		"fees":                float64(590),
		"tax":                 float64(90),
		"currency":            "INR",
		"settle_full_balance": false,
		"status":              "initiated",
		"description":         "Need this to make vendor payments.",
		"notes": map[string]interface{}{
			"notes_key_1": "Tea, Earl Grey, Hot",
			"notes_key_2": "Tea, Earl Grey… decaf.",
		},
		"created_at": float64(1596771429),
	}

	// Error response for insufficient amount
	insufficientAmountResp := map[string]interface{}{
		"error": map[string]interface{}{
			"code":        "BAD_REQUEST_ERROR",
			"description": "Minimum amount that can be settled is ₹ 1.",
		},
	}

	tests := []RazorpayToolTestCase{
		{
			Name: "successful settlement creation with all parameters",
			Request: map[string]interface{}{
				"amount":              float64(200000),
				"settle_full_balance": false,
				"description":         "Need this to make vendor payments.",
				"notes": map[string]interface{}{
					"notes_key_1": "Tea, Earl Grey, Hot",
					"notes_key_2": "Tea, Earl Grey… decaf.",
				},
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     createInstantSettlementPath,
						Method:   "POST",
						Response: successfulSettlementResp,
					},
				)
			},
			ExpectError:    false,
			ExpectedResult: successfulSettlementResp,
		},
		{
			Name: "settlement creation with required parameters only",
			Request: map[string]interface{}{
				"amount": float64(200000),
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     createInstantSettlementPath,
						Method:   "POST",
						Response: successfulSettlementResp,
					},
				)
			},
			ExpectError:    false,
			ExpectedResult: successfulSettlementResp,
		},
		{
			Name: "settlement creation with insufficient amount",
			Request: map[string]interface{}{
				"amount": float64(10), // Less than minimum
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     createInstantSettlementPath,
						Method:   "POST",
						Response: insufficientAmountResp,
					},
				)
			},
			ExpectError: true,
			ExpectedErrMsg: "creating instant settlement failed: Minimum amount that " +
				"can be settled is ₹ 1.",
		},
		{
			Name:           "missing amount parameter",
			Request:        map[string]interface{}{},
			MockHttpClient: nil, // No HTTP client needed for validation error
			ExpectError:    true,
			ExpectedErrMsg: "missing required parameter: amount",
		},
	}

	for _, tc := range tests {
		t.Run(tc.Name, func(t *testing.T) {
			runToolTest(t, tc, CreateInstantSettlement, "Instant Settlement")
		})
	}
}

func Test_FetchAllInstantSettlements(t *testing.T) {
	fetchAllInstantSettlementsPath := fmt.Sprintf(
		"/%s%s/ondemand",
		constants.VERSION_V1,
		constants.SETTLEMENT_URL,
	)

	// Sample response for successful fetch without expanded payouts
	basicSettlementListResp := map[string]interface{}{
		"entity": "collection",
		"count":  float64(2),
		"items": []interface{}{
			map[string]interface{}{
				"id":                  "setlod_FNj7g2YS5J67Rz",
				"entity":              "settlement.ondemand",
				"amount_requested":    float64(200000),
				"amount_settled":      float64(199410),
				"amount_pending":      float64(0),
				"amount_reversed":     float64(0),
				"fees":                float64(590),
				"tax":                 float64(90),
				"currency":            "INR",
				"settle_full_balance": false,
				"status":              "processed",
				"description":         "Need this to make vendor payments.",
				"notes": map[string]interface{}{
					"notes_key_1": "Tea, Earl Grey, Hot",
					"notes_key_2": "Tea, Earl Grey… decaf.",
				},
				"created_at": float64(1596771429),
			},
			map[string]interface{}{
				"id":                  "setlod_FJOp0jOWlalIvt",
				"entity":              "settlement.ondemand",
				"amount_requested":    float64(300000),
				"amount_settled":      float64(299114),
				"amount_pending":      float64(0),
				"amount_reversed":     float64(0),
				"fees":                float64(886),
				"tax":                 float64(136),
				"currency":            "INR",
				"settle_full_balance": false,
				"status":              "processed",
				"description":         "Need this to buy stock.",
				"notes": map[string]interface{}{
					"notes_key_1": "Tea, Earl Grey, Hot",
					"notes_key_2": "Tea, Earl Grey… decaf.",
				},
				"created_at": float64(1595826576),
			},
		},
	}

	// Sample response with expanded payouts
	expandedSettlementListResp := map[string]interface{}{
		"entity": "collection",
		"count":  float64(2),
		"items": []interface{}{
			map[string]interface{}{
				"id":                  "setlod_FNj7g2YS5J67Rz",
				"entity":              "settlement.ondemand",
				"amount_requested":    float64(200000),
				"amount_settled":      float64(199410),
				"amount_pending":      float64(0),
				"amount_reversed":     float64(0),
				"fees":                float64(590),
				"tax":                 float64(90),
				"currency":            "INR",
				"settle_full_balance": false,
				"status":              "processed",
				"description":         "Need this to make vendor payments.",
				"notes": map[string]interface{}{
					"notes_key_1": "Tea, Earl Grey, Hot",
					"notes_key_2": "Tea, Earl Grey… decaf.",
				},
				"created_at": float64(1596771429),
				"ondemand_payouts": []interface{}{
					map[string]interface{}{
						"id":     "pout_FNj7g2YS5J67Rz",
						"entity": "payout",
						"amount": float64(199410),
						"status": "processed",
					},
				},
			},
			map[string]interface{}{
				"id":                  "setlod_FJOp0jOWlalIvt",
				"entity":              "settlement.ondemand",
				"amount_requested":    float64(300000),
				"amount_settled":      float64(299114),
				"amount_pending":      float64(0),
				"amount_reversed":     float64(0),
				"fees":                float64(886),
				"tax":                 float64(136),
				"currency":            "INR",
				"settle_full_balance": false,
				"status":              "processed",
				"description":         "Need this to buy stock.",
				"notes": map[string]interface{}{
					"notes_key_1": "Tea, Earl Grey, Hot",
					"notes_key_2": "Tea, Earl Grey… decaf.",
				},
				"created_at": float64(1595826576),
				"ondemand_payouts": []interface{}{
					map[string]interface{}{
						"id":     "pout_FJOp0jOWlalIvt",
						"entity": "payout",
						"amount": float64(299114),
						"status": "processed",
					},
				},
			},
		},
	}

	// Error response when parameters are invalid
	invalidParamsResp := map[string]interface{}{
		"error": map[string]interface{}{
			"code":        "BAD_REQUEST_ERROR",
			"description": "from must be between 946684800 and 4765046400",
		},
	}

	tests := []RazorpayToolTestCase{
		{
			Name:    "successful instant settlements fetch with no parameters",
			Request: map[string]interface{}{},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     fetchAllInstantSettlementsPath,
						Method:   "GET",
						Response: basicSettlementListResp,
					},
				)
			},
			ExpectError:    false,
			ExpectedResult: basicSettlementListResp,
		},
		{
			Name: "instant settlements fetch with pagination",
			Request: map[string]interface{}{
				"count": float64(10),
				"skip":  float64(0),
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     fetchAllInstantSettlementsPath,
						Method:   "GET",
						Response: basicSettlementListResp,
					},
				)
			},
			ExpectError:    false,
			ExpectedResult: basicSettlementListResp,
		},
		{
			Name: "instant settlements fetch with expanded payouts",
			Request: map[string]interface{}{
				"expand": []interface{}{"ondemand_payouts"},
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     fetchAllInstantSettlementsPath,
						Method:   "GET",
						Response: expandedSettlementListResp,
					},
				)
			},
			ExpectError:    false,
			ExpectedResult: expandedSettlementListResp,
		},
		{
			Name: "instant settlements fetch with date range",
			Request: map[string]interface{}{
				"from": float64(1609459200), // 2021-01-01
				"to":   float64(1640995199), // 2021-12-31
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     fetchAllInstantSettlementsPath,
						Method:   "GET",
						Response: basicSettlementListResp,
					},
				)
			},
			ExpectError:    false,
			ExpectedResult: basicSettlementListResp,
		},
		{
			Name: "instant settlements fetch with invalid timestamp",
			Request: map[string]interface{}{
				"from": float64(900000000), // Invalid timestamp (too early)
				"to":   float64(1600000000),
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     fetchAllInstantSettlementsPath,
						Method:   "GET",
						Response: invalidParamsResp,
					},
				)
			},
			ExpectError: true,
			ExpectedErrMsg: "fetching instant settlements failed: from must be " +
				"between 946684800 and 4765046400",
		},
	}

	for _, tc := range tests {
		t.Run(tc.Name, func(t *testing.T) {
			runToolTest(t, tc, FetchAllInstantSettlements, "Instant Settlements List")
		})
	}
}

func Test_FetchInstantSettlement(t *testing.T) {
	fetchInstantSettlementPathFmt := fmt.Sprintf(
		"/%s%s/ondemand/%%s",
		constants.VERSION_V1,
		constants.SETTLEMENT_URL,
	)

	instantSettlementResp := map[string]interface{}{
		"id":                  "setlod_FNj7g2YS5J67Rz",
		"entity":              "settlement.ondemand",
		"amount_requested":    float64(200000),
		"amount_settled":      float64(199410),
		"amount_pending":      float64(0),
		"amount_reversed":     float64(0),
		"fees":                float64(590),
		"tax":                 float64(90),
		"currency":            "INR",
		"settle_full_balance": false,
		"status":              "processed",
		"description":         "Need this to make vendor payments.",
		"notes": map[string]interface{}{
			"notes_key_1": "Tea, Earl Grey, Hot",
			"notes_key_2": "Tea, Earl Grey… decaf.",
		},
		"created_at": float64(1596771429),
	}

	instantSettlementNotFoundResp := map[string]interface{}{
		"error": map[string]interface{}{
			"code":        "BAD_REQUEST_ERROR",
			"description": "instant settlement not found",
		},
	}

	tests := []RazorpayToolTestCase{
		{
			Name: "successful instant settlement fetch",
			Request: map[string]interface{}{
				"settlement_id": "setlod_FNj7g2YS5J67Rz",
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path: fmt.Sprintf(fetchInstantSettlementPathFmt,
							"setlod_FNj7g2YS5J67Rz"),
						Method:   "GET",
						Response: instantSettlementResp,
					},
				)
			},
			ExpectError:    false,
			ExpectedResult: instantSettlementResp,
		},
		{
			Name: "instant settlement not found",
			Request: map[string]interface{}{
				"settlement_id": "setlod_invalid",
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     fmt.Sprintf(fetchInstantSettlementPathFmt, "setlod_invalid"),
						Method:   "GET",
						Response: instantSettlementNotFoundResp,
					},
				)
			},
			ExpectError: true,
			ExpectedErrMsg: "fetching instant settlement failed: " +
				"instant settlement not found",
		},
		{
			Name:           "missing settlement_id parameter",
			Request:        map[string]interface{}{},
			MockHttpClient: nil, // No HTTP client needed for validation error
			ExpectError:    true,
			ExpectedErrMsg: "missing required parameter: settlement_id",
		},
	}

	for _, tc := range tests {
		t.Run(tc.Name, func(t *testing.T) {
			runToolTest(t, tc, FetchInstantSettlement, "Instant Settlement")
		})
	}
}

```

--------------------------------------------------------------------------------
/pkg/razorpay/qr_codes_test.go:
--------------------------------------------------------------------------------

```go
package razorpay

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/razorpay/razorpay-go/constants"

	"github.com/razorpay/razorpay-mcp-server/pkg/razorpay/mock"
)

func Test_CreateQRCode(t *testing.T) {
	createQRCodePath := fmt.Sprintf(
		"/%s%s",
		constants.VERSION_V1,
		constants.QRCODE_URL,
	)

	qrCodeWithAllParamsResp := map[string]interface{}{
		"id":                       "qr_HMsVL8HOpbMcjU",
		"entity":                   "qr_code",
		"created_at":               float64(1623660301),
		"name":                     "Store Front Display",
		"usage":                    "single_use",
		"type":                     "upi_qr",
		"image_url":                "https://rzp.io/i/BWcUVrLp",
		"payment_amount":           float64(300),
		"status":                   "active",
		"description":              "For Store 1",
		"fixed_amount":             true,
		"payments_amount_received": float64(0),
		"payments_count_received":  float64(0),
		"notes": map[string]interface{}{
			"purpose": "Test UPI QR Code notes",
		},
		"customer_id": "cust_HKsR5se84c5LTO",
		"close_by":    float64(1681615838),
	}

	qrCodeWithRequiredParamsResp := map[string]interface{}{
		"id":                       "qr_HMsVL8HOpbMcjU",
		"entity":                   "qr_code",
		"created_at":               float64(1623660301),
		"usage":                    "multiple_use",
		"type":                     "upi_qr",
		"image_url":                "https://rzp.io/i/BWcUVrLp",
		"status":                   "active",
		"fixed_amount":             false,
		"payments_amount_received": float64(0),
		"payments_count_received":  float64(0),
	}

	qrCodeWithoutPaymentAmountResp := map[string]interface{}{
		"id":                       "qr_HMsVL8HOpbMcjU",
		"entity":                   "qr_code",
		"created_at":               float64(1623660301),
		"name":                     "Store Front Display",
		"usage":                    "single_use",
		"type":                     "upi_qr",
		"image_url":                "https://rzp.io/i/BWcUVrLp",
		"status":                   "active",
		"description":              "For Store 1",
		"fixed_amount":             false,
		"payments_amount_received": float64(0),
		"payments_count_received":  float64(0),
	}

	errorResp := map[string]interface{}{
		"error": map[string]interface{}{
			"code":        "BAD_REQUEST_ERROR",
			"description": "The type field is invalid",
		},
	}

	tests := []RazorpayToolTestCase{
		{
			Name: "successful QR code creation with all parameters",
			Request: map[string]interface{}{
				"type":           "upi_qr",
				"name":           "Store Front Display",
				"usage":          "single_use",
				"fixed_amount":   true,
				"payment_amount": float64(300),
				"description":    "For Store 1",
				"customer_id":    "cust_HKsR5se84c5LTO",
				"close_by":       float64(1681615838),
				"notes": map[string]interface{}{
					"purpose": "Test UPI QR Code notes",
				},
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     createQRCodePath,
						Method:   "POST",
						Response: qrCodeWithAllParamsResp,
					},
				)
			},
			ExpectError:    false,
			ExpectedResult: qrCodeWithAllParamsResp,
		},
		{
			Name: "successful QR code creation with required params only",
			Request: map[string]interface{}{
				"type":  "upi_qr",
				"usage": "multiple_use",
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     createQRCodePath,
						Method:   "POST",
						Response: qrCodeWithRequiredParamsResp,
					},
				)
			},
			ExpectError:    false,
			ExpectedResult: qrCodeWithRequiredParamsResp,
		},
		{
			Name: "successful QR code creation without payment amount",
			Request: map[string]interface{}{
				"type":         "upi_qr",
				"name":         "Store Front Display",
				"usage":        "single_use",
				"fixed_amount": false,
				"description":  "For Store 1",
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     createQRCodePath,
						Method:   "POST",
						Response: qrCodeWithoutPaymentAmountResp,
					},
				)
			},
			ExpectError:    false,
			ExpectedResult: qrCodeWithoutPaymentAmountResp,
		},
		{
			Name: "missing required type parameter",
			Request: map[string]interface{}{
				"usage": "single_use",
			},
			MockHttpClient: nil,
			ExpectError:    true,
			ExpectedErrMsg: "missing required parameter: type",
		},
		{
			Name: "missing required usage parameter",
			Request: map[string]interface{}{
				"type": "upi_qr",
			},
			MockHttpClient: nil,
			ExpectError:    true,
			ExpectedErrMsg: "missing required parameter: usage",
		},
		{
			Name: "validator error - invalid parameter type",
			Request: map[string]interface{}{
				"type":  123,
				"usage": "single_use",
			},
			MockHttpClient: nil,
			ExpectError:    true,
			ExpectedErrMsg: "Validation errors",
		},
		{
			Name: "fixed_amount true but payment_amount missing",
			Request: map[string]interface{}{
				"type":         "upi_qr",
				"usage":        "single_use",
				"fixed_amount": true,
			},
			MockHttpClient: nil,
			ExpectError:    true,
			ExpectedErrMsg: "payment_amount is required when fixed_amount is true",
		},
		{
			Name: "invalid type parameter",
			Request: map[string]interface{}{
				"type":  "invalid_type",
				"usage": "single_use",
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     createQRCodePath,
						Method:   "POST",
						Response: errorResp,
					},
				)
			},
			ExpectError:    true,
			ExpectedErrMsg: "creating QR code failed: The type field is invalid",
		},
	}

	for _, tc := range tests {
		t.Run(tc.Name, func(t *testing.T) {
			runToolTest(t, tc, CreateQRCode, "QR Code")
		})
	}
}

func Test_FetchAllQRCodes(t *testing.T) {
	qrCodesPath := fmt.Sprintf(
		"/%s%s",
		constants.VERSION_V1,
		constants.QRCODE_URL,
	)

	allQRCodesResp := map[string]interface{}{
		"entity": "collection",
		"count":  float64(2),
		"items": []interface{}{
			map[string]interface{}{
				"id":                       "qr_HO2jGkWReVBMNu",
				"entity":                   "qr_code",
				"created_at":               float64(1623914648),
				"name":                     "Store_1",
				"usage":                    "single_use",
				"type":                     "upi_qr",
				"image_url":                "https://rzp.io/i/w2CEwYmkAu",
				"payment_amount":           float64(300),
				"status":                   "active",
				"description":              "For Store 1",
				"fixed_amount":             true,
				"payments_amount_received": float64(0),
				"payments_count_received":  float64(0),
				"notes": map[string]interface{}{
					"purpose": "Test UPI QR Code notes",
				},
				"customer_id":  "cust_HKsR5se84c5LTO",
				"close_by":     float64(1681615838),
				"closed_at":    nil,
				"close_reason": nil,
			},
			map[string]interface{}{
				"id":                       "qr_HO2e0813YlchUn",
				"entity":                   "qr_code",
				"created_at":               float64(1623914349),
				"name":                     "Acme Groceries",
				"usage":                    "multiple_use",
				"type":                     "upi_qr",
				"image_url":                "https://rzp.io/i/X6QM7LL",
				"payment_amount":           nil,
				"status":                   "closed",
				"description":              "Buy fresh groceries",
				"fixed_amount":             false,
				"payments_amount_received": float64(200),
				"payments_count_received":  float64(1),
				"notes": map[string]interface{}{
					"Branch": "Bangalore - Rajaji Nagar",
				},
				"customer_id":  "cust_HKsR5se84c5LTO",
				"close_by":     float64(1625077799),
				"closed_at":    float64(1623914515),
				"close_reason": "on_demand",
			},
		},
	}

	errorResp := map[string]interface{}{
		"error": map[string]interface{}{
			"code":        "BAD_REQUEST_ERROR",
			"description": "The query parameters are invalid",
		},
	}

	tests := []RazorpayToolTestCase{
		{
			Name:    "successful fetch all QR codes with no parameters",
			Request: map[string]interface{}{},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     qrCodesPath,
						Method:   "GET",
						Response: allQRCodesResp,
					},
				)
			},
			ExpectError:    false,
			ExpectedResult: allQRCodesResp,
		},
		{
			Name: "successful fetch all QR codes with count parameter",
			Request: map[string]interface{}{
				"count": float64(2),
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     qrCodesPath,
						Method:   "GET",
						Response: allQRCodesResp,
					},
				)
			},
			ExpectError:    false,
			ExpectedResult: allQRCodesResp,
		},
		{
			Name: "successful fetch all QR codes with pagination parameters",
			Request: map[string]interface{}{
				"from":  float64(1622000000),
				"to":    float64(1625000000),
				"count": float64(2),
				"skip":  float64(0),
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     qrCodesPath,
						Method:   "GET",
						Response: allQRCodesResp,
					},
				)
			},
			ExpectError:    false,
			ExpectedResult: allQRCodesResp,
		},
		{
			Name: "invalid parameters - caught by SDK",
			Request: map[string]interface{}{
				"count": float64(-1),
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:   qrCodesPath,
						Method: "GET",
						Response: map[string]interface{}{
							"error": map[string]interface{}{
								"code":        "BAD_REQUEST_ERROR",
								"description": "The count value should be greater than or equal to 1",
							},
						},
					},
				)
			},
			ExpectError: true,
			ExpectedErrMsg: "fetching QR codes failed: " +
				"The count value should be greater than or equal to 1",
		},
		{
			Name: "validator error - invalid count parameter type",
			Request: map[string]interface{}{
				"count": "not-a-number",
			},
			MockHttpClient: nil,
			ExpectError:    true,
			ExpectedErrMsg: "Validation errors",
		},
		{
			Name: "API error response",
			Request: map[string]interface{}{
				"count": float64(1000),
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     qrCodesPath,
						Method:   "GET",
						Response: errorResp,
					},
				)
			},
			ExpectError:    true,
			ExpectedErrMsg: "fetching QR codes failed: The query parameters are invalid",
		},
	}

	for _, tc := range tests {
		t.Run(tc.Name, func(t *testing.T) {
			runToolTest(t, tc, FetchAllQRCodes, "QR Codes")
		})
	}
}

func Test_FetchQRCodesByCustomerID(t *testing.T) {
	qrCodesPath := fmt.Sprintf(
		"/%s%s",
		constants.VERSION_V1,
		constants.QRCODE_URL,
	)

	customerQRCodesResp := map[string]interface{}{
		"entity": "collection",
		"count":  float64(1),
		"items": []interface{}{
			map[string]interface{}{
				"id":                       "qr_HMsgvioW64f0vh",
				"entity":                   "qr_code",
				"created_at":               float64(1623660959),
				"name":                     "Store_1",
				"usage":                    "single_use",
				"type":                     "upi_qr",
				"image_url":                "https://rzp.io/i/DTa2eQR",
				"payment_amount":           float64(300),
				"status":                   "active",
				"description":              "For Store 1",
				"fixed_amount":             true,
				"payments_amount_received": float64(0),
				"payments_count_received":  float64(0),
				"notes": map[string]interface{}{
					"purpose": "Test UPI QR Code notes",
				},
				"customer_id":  "cust_HKsR5se84c5LTO",
				"close_by":     float64(1681615838),
				"closed_at":    nil,
				"close_reason": nil,
			},
		},
	}

	errorResp := map[string]interface{}{
		"error": map[string]interface{}{
			"code":        "BAD_REQUEST_ERROR",
			"description": "The id provided is not a valid id",
		},
	}

	tests := []RazorpayToolTestCase{
		{
			Name: "successful fetch QR codes by customer ID",
			Request: map[string]interface{}{
				"customer_id": "cust_HKsR5se84c5LTO",
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     qrCodesPath,
						Method:   "GET",
						Response: customerQRCodesResp,
					},
				)
			},
			ExpectError:    false,
			ExpectedResult: customerQRCodesResp,
		},
		{
			Name:           "missing required customer_id parameter",
			Request:        map[string]interface{}{},
			MockHttpClient: nil,
			ExpectError:    true,
			ExpectedErrMsg: "missing required parameter: customer_id",
		},
		{
			Name: "validator error - invalid customer_id parameter type",
			Request: map[string]interface{}{
				"customer_id": 12345,
			},
			MockHttpClient: nil,
			ExpectError:    true,
			ExpectedErrMsg: "invalid parameter type: customer_id",
		},
		{
			Name: "API error - invalid customer ID",
			Request: map[string]interface{}{
				"customer_id": "invalid_customer_id",
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     qrCodesPath,
						Method:   "GET",
						Response: errorResp,
					},
				)
			},
			ExpectError: true,
			ExpectedErrMsg: "fetching QR codes failed: " +
				"The id provided is not a valid id",
		},
	}

	for _, tc := range tests {
		t.Run(tc.Name, func(t *testing.T) {
			runToolTest(t, tc, FetchQRCodesByCustomerID, "QR Codes by Customer ID")
		})
	}
}

func Test_FetchQRCodesByPaymentID(t *testing.T) {
	qrCodesPath := fmt.Sprintf(
		"/%s%s",
		constants.VERSION_V1,
		constants.QRCODE_URL,
	)

	paymentQRCodesResp := map[string]interface{}{
		"entity": "collection",
		"count":  float64(1),
		"items": []interface{}{
			map[string]interface{}{
				"id":                       "qr_HMsqRoeVwKbwAF",
				"entity":                   "qr_code",
				"created_at":               float64(1623661499),
				"name":                     "Fresh Groceries",
				"usage":                    "multiple_use",
				"type":                     "upi_qr",
				"image_url":                "https://rzp.io/i/eI9XD54Q",
				"payment_amount":           nil,
				"status":                   "active",
				"description":              "Buy fresh groceries",
				"fixed_amount":             false,
				"payments_amount_received": float64(1000),
				"payments_count_received":  float64(1),
				"notes":                    []interface{}{},
				"customer_id":              "cust_HKsR5se84c5LTO",
				"close_by":                 float64(1624472999),
				"close_reason":             nil,
			},
		},
	}

	errorResp := map[string]interface{}{
		"error": map[string]interface{}{
			"code":        "BAD_REQUEST_ERROR",
			"description": "The id provided is not a valid id",
		},
	}

	tests := []RazorpayToolTestCase{
		{
			Name: "successful fetch QR codes by payment ID",
			Request: map[string]interface{}{
				"payment_id": "pay_Di5iqCqA1WEHq6",
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     qrCodesPath,
						Method:   "GET",
						Response: paymentQRCodesResp,
					},
				)
			},
			ExpectError:    false,
			ExpectedResult: paymentQRCodesResp,
		},
		{
			Name:           "missing required payment_id parameter",
			Request:        map[string]interface{}{},
			MockHttpClient: nil,
			ExpectError:    true,
			ExpectedErrMsg: "missing required parameter: payment_id",
		},
		{
			Name: "validator error - invalid payment_id parameter type",
			Request: map[string]interface{}{
				"payment_id": 12345,
			},
			MockHttpClient: nil,
			ExpectError:    true,
			ExpectedErrMsg: "invalid parameter type: payment_id",
		},
		{
			Name: "API error - invalid payment ID",
			Request: map[string]interface{}{
				"payment_id": "invalid_payment_id",
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     qrCodesPath,
						Method:   "GET",
						Response: errorResp,
					},
				)
			},
			ExpectError: true,
			ExpectedErrMsg: "fetching QR codes failed: " +
				"The id provided is not a valid id",
		},
	}

	for _, tc := range tests {
		t.Run(tc.Name, func(t *testing.T) {
			runToolTest(t, tc, FetchQRCodesByPaymentID, "QR Codes by Payment ID")
		})
	}
}

func TestFetchQRCode(t *testing.T) {
	// Initialize necessary variables
	qrID := "qr_FuZIYx6rMbP6gs"
	apiPath := fmt.Sprintf(
		"/%s%s/%s",
		constants.VERSION_V1,
		constants.QRCODE_URL,
		qrID,
	)

	// Successful response based on Razorpay docs
	successResponse := map[string]interface{}{
		"id":                       qrID,
		"entity":                   "qr_code",
		"created_at":               float64(1623915088),
		"name":                     "Store_1",
		"usage":                    "single_use",
		"type":                     "upi_qr",
		"image_url":                "https://rzp.io/i/oCswTOcCo",
		"payment_amount":           float64(300),
		"status":                   "active",
		"description":              "For Store 1",
		"fixed_amount":             true,
		"payments_amount_received": float64(0),
		"payments_count_received":  float64(0),
		"notes": map[string]interface{}{
			"purpose": "Test UPI QR Code notes",
		},
		"customer_id":  "cust_HKsR5se84c5LTO",
		"close_by":     float64(1681615838),
		"closed_at":    nil,
		"close_reason": nil,
	}

	errorResp := map[string]interface{}{
		"error": map[string]interface{}{
			"code":        "BAD_REQUEST_ERROR",
			"description": "The QR code ID provided is invalid",
		},
	}

	tests := []RazorpayToolTestCase{
		{
			Name: "successful fetch QR code by ID",
			Request: map[string]interface{}{
				"qr_code_id": qrID,
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     apiPath,
						Method:   "GET",
						Response: successResponse,
					},
				)
			},
			ExpectError:    false,
			ExpectedResult: successResponse,
		},
		{
			Name:           "missing required qr_code_id parameter",
			Request:        map[string]interface{}{},
			MockHttpClient: nil,
			ExpectError:    true,
			ExpectedErrMsg: "missing required parameter: qr_code_id",
		},
		{
			Name: "validator error - invalid qr_code_id parameter type",
			Request: map[string]interface{}{
				"qr_code_id": 12345,
			},
			MockHttpClient: nil,
			ExpectError:    true,
			ExpectedErrMsg: "invalid parameter type: qr_code_id",
		},
		{
			Name: "API error - invalid QR code ID",
			Request: map[string]interface{}{
				"qr_code_id": qrID,
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     apiPath,
						Method:   "GET",
						Response: errorResp,
					},
				)
			},
			ExpectError: true,
			ExpectedErrMsg: "fetching QR code failed: " +
				"The QR code ID provided is invalid",
		},
	}

	for _, tc := range tests {
		t.Run(tc.Name, func(t *testing.T) {
			runToolTest(t, tc, FetchQRCode, "QR Code")
		})
	}
}

func TestFetchPaymentsForQRCode(t *testing.T) {
	apiPath := "/" + constants.VERSION_V1 +
		constants.QRCODE_URL + "/qr_test123/payments"

	successResponse := map[string]interface{}{
		"entity": "collection",
		"count":  float64(2),
		"items": []interface{}{
			map[string]interface{}{
				"id":              "pay_test123",
				"entity":          "payment",
				"amount":          float64(500),
				"currency":        "INR",
				"status":          "captured",
				"method":          "upi",
				"amount_refunded": float64(0),
				"refund_status":   nil,
				"captured":        true,
				"description":     "QRv2 Payment",
				"customer_id":     "cust_test123",
				"created_at":      float64(1623662800),
			},
			map[string]interface{}{
				"id":              "pay_test456",
				"entity":          "payment",
				"amount":          float64(1000),
				"currency":        "INR",
				"status":          "refunded",
				"method":          "upi",
				"amount_refunded": float64(1000),
				"refund_status":   "full",
				"captured":        true,
				"description":     "QRv2 Payment",
				"customer_id":     "cust_test123",
				"created_at":      float64(1623661533),
			},
		},
	}

	tests := []RazorpayToolTestCase{
		{
			Name: "successful fetch payments for QR code",
			Request: map[string]interface{}{
				"qr_code_id": "qr_test123",
				"count":      10,
				"from":       1623661000,
				"to":         1623663000,
				"skip":       0,
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     apiPath,
						Method:   "GET",
						Response: successResponse,
					},
				)
			},
			ExpectError:    false,
			ExpectedResult: successResponse,
		},
		{
			Name: "missing required parameter",
			Request: map[string]interface{}{
				"count": 10,
			},
			MockHttpClient: nil,
			ExpectError:    true,
			ExpectedErrMsg: "missing required parameter: qr_code_id",
		},
		{
			Name: "invalid parameter type",
			Request: map[string]interface{}{
				"qr_code_id": 123,
			},
			MockHttpClient: nil,
			ExpectError:    true,
			ExpectedErrMsg: "invalid parameter type: qr_code_id",
		},
		{
			Name: "API error",
			Request: map[string]interface{}{
				"qr_code_id": "qr_test123",
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:   apiPath,
						Method: "GET",
						Response: map[string]interface{}{
							"error": map[string]interface{}{
								"code":        "BAD_REQUEST_ERROR",
								"description": "mock error",
							},
						},
					},
				)
			},
			ExpectError:    true,
			ExpectedErrMsg: "fetching payments for QR code failed: mock error",
		},
	}

	for _, tc := range tests {
		t.Run(tc.Name, func(t *testing.T) {
			runToolTest(t, tc, FetchPaymentsForQRCode, "QR Code Payments")
		})
	}
}

func TestCloseQRCode(t *testing.T) {
	successResponse := map[string]interface{}{
		"id":                       "qr_HMsVL8HOpbMcjU",
		"entity":                   "qr_code",
		"created_at":               float64(1623660301),
		"name":                     "Store_1",
		"usage":                    "single_use",
		"type":                     "upi_qr",
		"image_url":                "https://rzp.io/i/BWcUVrLp",
		"payment_amount":           float64(300),
		"status":                   "closed",
		"description":              "For Store 1",
		"fixed_amount":             true,
		"payments_amount_received": float64(0),
		"payments_count_received":  float64(0),
		"notes": map[string]interface{}{
			"purpose": "Test UPI QR Code notes",
		},
		"customer_id":  "cust_HKsR5se84c5LTO",
		"close_by":     float64(1681615838),
		"closed_at":    float64(1623660445),
		"close_reason": "on_demand",
	}

	baseAPIPath := fmt.Sprintf("/%s%s", constants.VERSION_V1, constants.QRCODE_URL)
	qrCodeID := "qr_HMsVL8HOpbMcjU"
	apiPath := fmt.Sprintf("%s/%s/close", baseAPIPath, qrCodeID)

	tests := []RazorpayToolTestCase{
		{
			Name: "successful close QR code",
			Request: map[string]interface{}{
				"qr_code_id": qrCodeID,
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     apiPath,
						Method:   "POST",
						Response: successResponse,
					},
				)
			},
			ExpectError:    false,
			ExpectedResult: successResponse,
		},
		{
			Name:           "missing required qr_code_id parameter",
			Request:        map[string]interface{}{},
			MockHttpClient: nil,
			ExpectError:    true,
			ExpectedErrMsg: "missing required parameter: qr_code_id",
		},
	}

	for _, tc := range tests {
		t.Run(tc.Name, func(t *testing.T) {
			runToolTest(t, tc, CloseQRCode, "QR Code")
		})
	}
}

```

--------------------------------------------------------------------------------
/pkg/razorpay/orders_test.go:
--------------------------------------------------------------------------------

```go
package razorpay

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/razorpay/razorpay-go/constants"

	"github.com/razorpay/razorpay-mcp-server/pkg/razorpay/mock"
)

func Test_CreateOrder(t *testing.T) {
	createOrderPath := fmt.Sprintf(
		"/%s%s",
		constants.VERSION_V1,
		constants.ORDER_URL,
	)

	// Define common response maps to be reused
	orderWithAllParamsResp := map[string]interface{}{
		"id":                       "order_EKwxwAgItmmXdp",
		"amount":                   float64(10000),
		"currency":                 "INR",
		"receipt":                  "receipt-123",
		"partial_payment":          true,
		"first_payment_min_amount": float64(5000),
		"notes": map[string]interface{}{
			"customer_name": "test-customer",
			"product_name":  "test-product",
		},
		"status": "created",
	}

	orderWithRequiredParamsResp := map[string]interface{}{
		"id":       "order_EKwxwAgItmmXdp",
		"amount":   float64(10000),
		"currency": "INR",
		"status":   "created",
	}

	errorResp := map[string]interface{}{
		"error": map[string]interface{}{
			"code":        "BAD_REQUEST_ERROR",
			"description": "Razorpay API error: Bad request",
		},
	}

	tests := []RazorpayToolTestCase{
		{
			Name: "successful order creation with all parameters",
			Request: map[string]interface{}{
				"amount":                   float64(10000),
				"currency":                 "INR",
				"receipt":                  "receipt-123",
				"partial_payment":          true,
				"first_payment_min_amount": float64(5000),
				"notes": map[string]interface{}{
					"customer_name": "test-customer",
					"product_name":  "test-product",
				},
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     createOrderPath,
						Method:   "POST",
						Response: orderWithAllParamsResp,
					},
				)
			},
			ExpectError:    false,
			ExpectedResult: orderWithAllParamsResp,
		},
		{
			Name: "successful order creation with required params only",
			Request: map[string]interface{}{
				"amount":   float64(10000),
				"currency": "INR",
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     createOrderPath,
						Method:   "POST",
						Response: orderWithRequiredParamsResp,
					},
				)
			},
			ExpectError:    false,
			ExpectedResult: orderWithRequiredParamsResp,
		},
		{
			Name: "multiple validation errors",
			Request: map[string]interface{}{
				// Missing both amount and currency (required parameters)
				"partial_payment":          "invalid_boolean", // Wrong type for boolean
				"first_payment_min_amount": "invalid_number",  // Wrong type for number
			},
			MockHttpClient: nil, // No HTTP client needed for validation error
			ExpectError:    true,
			ExpectedErrMsg: "Validation errors:\n- " +
				"missing required parameter: amount\n- " +
				"missing required parameter: currency\n- " +
				"invalid parameter type: partial_payment",
		},
		{
			Name: "first_payment_min_amount validation when partial_payment is true",
			Request: map[string]interface{}{
				"amount":                   float64(10000),
				"currency":                 "INR",
				"partial_payment":          true,
				"first_payment_min_amount": "invalid_number",
			},
			MockHttpClient: nil, // No HTTP client needed for validation error
			ExpectError:    true,
			ExpectedErrMsg: "Validation errors:\n- " +
				"invalid parameter type: first_payment_min_amount",
		},
		{
			Name: "order creation fails",
			Request: map[string]interface{}{
				"amount":   float64(10000),
				"currency": "INR",
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     createOrderPath,
						Method:   "POST",
						Response: errorResp,
					},
				)
			},
			ExpectError:    true,
			ExpectedErrMsg: "creating order failed: Razorpay API error: Bad request",
		},
		{
			Name: "successful SBMD mandate order creation",
			Request: map[string]interface{}{
				"amount":      float64(500000),
				"currency":    "INR",
				"customer_id": "cust_4xbQrmEoA5WJ01",
				"method":      "upi",
				"token": map[string]interface{}{
					"max_amount": float64(500000),
					"expire_at":  float64(2709971120),
					"frequency":  "as_presented",
					"type":       "single_block_multiple_debit",
				},
				"receipt": "Receipt No. 1",
				"notes": map[string]interface{}{
					"notes_key_1": "Tea, Earl Grey, Hot",
					"notes_key_2": "Tea, Earl Grey... decaf.",
				},
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				sbmdOrderResp := map[string]interface{}{
					"id":          "order_SBMD123456",
					"amount":      float64(500000),
					"currency":    "INR",
					"customer_id": "cust_4xbQrmEoA5WJ01",
					"method":      "upi",
					"token": map[string]interface{}{
						"max_amount": float64(500000),
						"expire_at":  float64(2709971120),
						"frequency":  "as_presented",
						"type":       "single_block_multiple_debit",
					},
					"receipt": "Receipt No. 1",
					"status":  "created",
					"notes": map[string]interface{}{
						"notes_key_1": "Tea, Earl Grey, Hot",
						"notes_key_2": "Tea, Earl Grey... decaf.",
					},
				}
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     createOrderPath,
						Method:   "POST",
						Response: sbmdOrderResp,
					},
				)
			},
			ExpectError: false,
			ExpectedResult: map[string]interface{}{
				"id":          "order_SBMD123456",
				"amount":      float64(500000),
				"currency":    "INR",
				"customer_id": "cust_4xbQrmEoA5WJ01",
				"method":      "upi",
				"token": map[string]interface{}{
					"max_amount": float64(500000),
					"expire_at":  float64(2709971120),
					"frequency":  "as_presented",
					"type":       "single_block_multiple_debit",
				},
				"receipt": "Receipt No. 1",
				"status":  "created",
				"notes": map[string]interface{}{
					"notes_key_1": "Tea, Earl Grey, Hot",
					"notes_key_2": "Tea, Earl Grey... decaf.",
				},
			},
		},
		{
			Name: "mandate order with invalid token parameter type",
			Request: map[string]interface{}{
				"amount":      float64(500000),
				"currency":    "INR",
				"customer_id": "cust_4xbQrmEoA5WJ01",
				"method":      "upi",
				"token":       "invalid_token_should_be_object",
			},
			MockHttpClient: nil,
			ExpectError:    true,
			ExpectedErrMsg: "invalid parameter type: token",
		},
		{
			Name: "mandate order with invalid method parameter type",
			Request: map[string]interface{}{
				"amount":      float64(500000),
				"currency":    "INR",
				"customer_id": "cust_4xbQrmEoA5WJ01",
				"method":      123,
				"token": map[string]interface{}{
					"max_amount": float64(500000),
					"expire_at":  float64(2709971120),
					"frequency":  "as_presented",
				},
			},
			MockHttpClient: nil,
			ExpectError:    true,
			ExpectedErrMsg: "invalid parameter type: method",
		},
		{
			Name: "token validation - missing max_amount",
			Request: map[string]interface{}{
				"amount":      float64(500000),
				"currency":    "INR",
				"customer_id": "cust_4xbQrmEoA5WJ01",
				"method":      "upi",
				"token": map[string]interface{}{
					"expire_at": float64(2709971120),
					"frequency": "as_presented",
				},
			},
			MockHttpClient: nil,
			ExpectError:    true,
			ExpectedErrMsg: "token.max_amount is required",
		},
		{
			Name: "token validation - missing frequency",
			Request: map[string]interface{}{
				"amount":      float64(500000),
				"currency":    "INR",
				"customer_id": "cust_4xbQrmEoA5WJ01",
				"method":      "upi",
				"token": map[string]interface{}{
					"max_amount": float64(500000),
					"expire_at":  float64(2709971120),
				},
			},
			MockHttpClient: nil,
			ExpectError:    true,
			ExpectedErrMsg: "token.frequency is required",
		},
		{
			Name: "token validation - invalid max_amount type",
			Request: map[string]interface{}{
				"amount":      float64(500000),
				"currency":    "INR",
				"customer_id": "cust_4xbQrmEoA5WJ01",
				"method":      "upi",
				"token": map[string]interface{}{
					"max_amount": "invalid_string",
					"expire_at":  float64(2709971120),
					"frequency":  "as_presented",
				},
			},
			MockHttpClient: nil,
			ExpectError:    true,
			ExpectedErrMsg: "token.max_amount must be a number",
		},
		{
			Name: "token validation - invalid max_amount value",
			Request: map[string]interface{}{
				"amount":      float64(500000),
				"currency":    "INR",
				"customer_id": "cust_4xbQrmEoA5WJ01",
				"method":      "upi",
				"token": map[string]interface{}{
					"max_amount": float64(-100),
					"expire_at":  float64(2709971120),
					"frequency":  "as_presented",
				},
			},
			MockHttpClient: nil,
			ExpectError:    true,
			ExpectedErrMsg: "token.max_amount must be greater than 0",
		},
		{
			Name: "token validation - invalid expire_at type",
			Request: map[string]interface{}{
				"amount":      float64(500000),
				"currency":    "INR",
				"customer_id": "cust_4xbQrmEoA5WJ01",
				"method":      "upi",
				"token": map[string]interface{}{
					"max_amount": float64(500000),
					"expire_at":  "invalid_string",
					"frequency":  "as_presented",
				},
			},
			MockHttpClient: nil,
			ExpectError:    true,
			ExpectedErrMsg: "token.expire_at must be a number",
		},
		{
			Name: "token validation - invalid expire_at value",
			Request: map[string]interface{}{
				"amount":      float64(500000),
				"currency":    "INR",
				"customer_id": "cust_4xbQrmEoA5WJ01",
				"method":      "upi",
				"token": map[string]interface{}{
					"max_amount": float64(500000),
					"expire_at":  float64(-100),
					"frequency":  "as_presented",
				},
			},
			MockHttpClient: nil,
			ExpectError:    true,
			ExpectedErrMsg: "token.expire_at must be greater than 0",
		},
		{
			Name: "token validation - invalid frequency type",
			Request: map[string]interface{}{
				"amount":      float64(500000),
				"currency":    "INR",
				"customer_id": "cust_4xbQrmEoA5WJ01",
				"method":      "upi",
				"token": map[string]interface{}{
					"max_amount": float64(500000),
					"expire_at":  float64(2709971120),
					"frequency":  123,
				},
			},
			MockHttpClient: nil,
			ExpectError:    true,
			ExpectedErrMsg: "token.frequency must be a string",
		},
		{
			Name: "token validation - invalid frequency value",
			Request: map[string]interface{}{
				"amount":      float64(500000),
				"currency":    "INR",
				"customer_id": "cust_4xbQrmEoA5WJ01",
				"method":      "upi",
				"token": map[string]interface{}{
					"max_amount": float64(500000),
					"expire_at":  float64(2709971120),
					"frequency":  "invalid_frequency",
				},
			},
			MockHttpClient: nil,
			ExpectError:    true,
			ExpectedErrMsg: "token.frequency must be one of: as_presented, " +
				"monthly, one_time, yearly, weekly, daily",
		},
		{
			Name: "token validation - invalid type value",
			Request: map[string]interface{}{
				"amount":      float64(500000),
				"currency":    "INR",
				"customer_id": "cust_4xbQrmEoA5WJ01",
				"method":      "upi",
				"token": map[string]interface{}{
					"max_amount": float64(500000),
					"expire_at":  float64(2709971120),
					"frequency":  "as_presented",
					"type":       "invalid_type",
				},
			},
			MockHttpClient: nil,
			ExpectError:    true,
			ExpectedErrMsg: "token.type must be one of: single_block_multiple_debit",
		},
		{
			Name: "token validation - invalid type type",
			Request: map[string]interface{}{
				"amount":      float64(500000),
				"currency":    "INR",
				"customer_id": "cust_4xbQrmEoA5WJ01",
				"method":      "upi",
				"token": map[string]interface{}{
					"max_amount": float64(500000),
					"expire_at":  float64(2709971120),
					"frequency":  "as_presented",
					"type":       123,
				},
			},
			MockHttpClient: nil,
			ExpectError:    true,
			ExpectedErrMsg: "token.type must be a string",
		},
		{
			Name: "token validation - missing type",
			Request: map[string]interface{}{
				"amount":      float64(500000),
				"currency":    "INR",
				"customer_id": "cust_4xbQrmEoA5WJ01",
				"method":      "upi",
				"token": map[string]interface{}{
					"max_amount": float64(500000),
					"expire_at":  float64(2709971120),
					"frequency":  "as_presented",
				},
			},
			MockHttpClient: nil,
			ExpectError:    true,
			ExpectedErrMsg: "token.type is required",
		},
		{
			Name: "token validation - default expire_at when not provided",
			Request: map[string]interface{}{
				"amount":      float64(500000),
				"currency":    "INR",
				"customer_id": "cust_4xbQrmEoA5WJ01",
				"method":      "upi",
				"token": map[string]interface{}{
					"max_amount": float64(500000),
					"frequency":  "as_presented",
					"type":       "single_block_multiple_debit",
				},
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:   createOrderPath,
						Method: "POST",
						Response: map[string]interface{}{
							"id": "order_test_12345",
						},
					},
				)
			},
			ExpectError: false,
			ExpectedResult: map[string]interface{}{
				"id": "order_test_12345",
			},
		},
	}

	for _, tc := range tests {
		t.Run(tc.Name, func(t *testing.T) {
			runToolTest(t, tc, CreateOrder, "Order")
		})
	}
}

func Test_FetchOrder(t *testing.T) {
	fetchOrderPathFmt := fmt.Sprintf(
		"/%s%s/%%s",
		constants.VERSION_V1,
		constants.ORDER_URL,
	)

	orderResp := map[string]interface{}{
		"id":       "order_EKwxwAgItmmXdp",
		"amount":   float64(10000),
		"currency": "INR",
		"receipt":  "receipt-123",
		"status":   "created",
	}

	orderNotFoundResp := map[string]interface{}{
		"error": map[string]interface{}{
			"code":        "BAD_REQUEST_ERROR",
			"description": "order not found",
		},
	}

	tests := []RazorpayToolTestCase{
		{
			Name: "successful order fetch",
			Request: map[string]interface{}{
				"order_id": "order_EKwxwAgItmmXdp",
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     fmt.Sprintf(fetchOrderPathFmt, "order_EKwxwAgItmmXdp"),
						Method:   "GET",
						Response: orderResp,
					},
				)
			},
			ExpectError:    false,
			ExpectedResult: orderResp,
		},
		{
			Name: "order not found",
			Request: map[string]interface{}{
				"order_id": "order_invalid",
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     fmt.Sprintf(fetchOrderPathFmt, "order_invalid"),
						Method:   "GET",
						Response: orderNotFoundResp,
					},
				)
			},
			ExpectError:    true,
			ExpectedErrMsg: "fetching order failed: order not found",
		},
		{
			Name:           "missing order_id parameter",
			Request:        map[string]interface{}{},
			MockHttpClient: nil, // No HTTP client needed for validation error
			ExpectError:    true,
			ExpectedErrMsg: "missing required parameter: order_id",
		},
	}

	for _, tc := range tests {
		t.Run(tc.Name, func(t *testing.T) {
			runToolTest(t, tc, FetchOrder, "Order")
		})
	}
}

func Test_FetchAllOrders(t *testing.T) {
	fetchAllOrdersPath := fmt.Sprintf(
		"/%s%s",
		constants.VERSION_V1,
		constants.ORDER_URL,
	)

	// Define the sample response for all orders
	ordersResp := map[string]interface{}{
		"entity": "collection",
		"count":  float64(2),
		"items": []interface{}{
			map[string]interface{}{
				"id":          "order_EKzX2WiEWbMxmx",
				"entity":      "order",
				"amount":      float64(1234),
				"amount_paid": float64(0),
				"amount_due":  float64(1234),
				"currency":    "INR",
				"receipt":     "Receipt No. 1",
				"offer_id":    nil,
				"status":      "created",
				"attempts":    float64(0),
				"notes":       []interface{}{},
				"created_at":  float64(1582637108),
			},
			map[string]interface{}{
				"id":          "order_EAI5nRfThga2TU",
				"entity":      "order",
				"amount":      float64(100),
				"amount_paid": float64(0),
				"amount_due":  float64(100),
				"currency":    "INR",
				"receipt":     "Receipt No. 1",
				"offer_id":    nil,
				"status":      "created",
				"attempts":    float64(0),
				"notes":       []interface{}{},
				"created_at":  float64(1580300731),
			},
		},
	}

	// Define error response
	errorResp := map[string]interface{}{
		"error": map[string]interface{}{
			"code":        "BAD_REQUEST_ERROR",
			"description": "Razorpay API error: Bad request",
		},
	}

	// Define the test cases
	tests := []RazorpayToolTestCase{
		{
			Name:    "successful fetch all orders with no parameters",
			Request: map[string]interface{}{},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     fetchAllOrdersPath,
						Method:   "GET",
						Response: ordersResp,
					},
				)
			},
			ExpectError:    false,
			ExpectedResult: ordersResp,
		},
		{
			Name: "successful fetch all orders with pagination",
			Request: map[string]interface{}{
				"count": 2,
				"skip":  1,
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     fetchAllOrdersPath,
						Method:   "GET",
						Response: ordersResp,
					},
				)
			},
			ExpectError:    false,
			ExpectedResult: ordersResp,
		},
		{
			Name: "successful fetch all orders with time range",
			Request: map[string]interface{}{
				"from": 1580000000,
				"to":   1590000000,
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     fetchAllOrdersPath,
						Method:   "GET",
						Response: ordersResp,
					},
				)
			},
			ExpectError:    false,
			ExpectedResult: ordersResp,
		},
		{
			Name: "successful fetch all orders with filtering",
			Request: map[string]interface{}{
				"authorized": 1,
				"receipt":    "Receipt No. 1",
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     fetchAllOrdersPath,
						Method:   "GET",
						Response: ordersResp,
					},
				)
			},
			ExpectError:    false,
			ExpectedResult: ordersResp,
		},
		{
			Name: "successful fetch all orders with expand",
			Request: map[string]interface{}{
				"expand": []interface{}{"payments"},
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     fetchAllOrdersPath,
						Method:   "GET",
						Response: ordersResp,
					},
				)
			},
			ExpectError:    false,
			ExpectedResult: ordersResp,
		},
		{
			Name: "multiple validation errors",
			Request: map[string]interface{}{
				"count":  "not-a-number",
				"skip":   "not-a-number",
				"from":   "not-a-number",
				"to":     "not-a-number",
				"expand": "not-an-array",
			},
			MockHttpClient: nil, // No HTTP client needed for validation error
			ExpectError:    true,
			ExpectedErrMsg: "Validation errors:\n- " +
				"invalid parameter type: count\n- " +
				"invalid parameter type: skip\n- " +
				"invalid parameter type: from\n- " +
				"invalid parameter type: to\n- " +
				"invalid parameter type: expand",
		},
		{
			Name: "fetch all orders fails",
			Request: map[string]interface{}{
				"count": 100,
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     fetchAllOrdersPath,
						Method:   "GET",
						Response: errorResp,
					},
				)
			},
			ExpectError:    true,
			ExpectedErrMsg: "fetching orders failed: Razorpay API error: Bad request",
		},
	}

	for _, tc := range tests {
		t.Run(tc.Name, func(t *testing.T) {
			runToolTest(t, tc, FetchAllOrders, "Order")
		})
	}
}

func Test_FetchOrderPayments(t *testing.T) {
	fetchOrderPaymentsPathFmt := fmt.Sprintf(
		"/%s%s/%%s/payments",
		constants.VERSION_V1,
		constants.ORDER_URL,
	)

	// Define the sample response for order payments
	paymentsResp := map[string]interface{}{
		"entity": "collection",
		"count":  float64(2),
		"items": []interface{}{
			map[string]interface{}{
				"id":              "pay_N8FUmetkCE2hZP",
				"entity":          "payment",
				"amount":          float64(100),
				"currency":        "INR",
				"status":          "failed",
				"order_id":        "order_N8FRN5zTm5S3wx",
				"invoice_id":      nil,
				"international":   false,
				"method":          "upi",
				"amount_refunded": float64(0),
				"refund_status":   nil,
				"captured":        false,
				"description":     nil,
				"card_id":         nil,
				"bank":            nil,
				"wallet":          nil,
				"vpa":             "failure@razorpay",
				"email":           "[email protected]",
				"contact":         "+919999999999",
				"notes": map[string]interface{}{
					"notes_key_1": "Tea, Earl Grey, Hot",
					"notes_key_2": "Tea, Earl Grey… decaf.",
				},
				"fee":               nil,
				"tax":               nil,
				"error_code":        "BAD_REQUEST_ERROR",
				"error_description": "Payment was unsuccessful due to a temporary issue.",
				"error_source":      "gateway",
				"error_step":        "payment_response",
				"error_reason":      "payment_failed",
				"acquirer_data": map[string]interface{}{
					"rrn": nil,
				},
				"created_at": float64(1701688684),
				"upi": map[string]interface{}{
					"vpa": "failure@razorpay",
				},
			},
			map[string]interface{}{
				"id":              "pay_N8FVRD1DzYzBh1",
				"entity":          "payment",
				"amount":          float64(100),
				"currency":        "INR",
				"status":          "captured",
				"order_id":        "order_N8FRN5zTm5S3wx",
				"invoice_id":      nil,
				"international":   false,
				"method":          "upi",
				"amount_refunded": float64(0),
				"refund_status":   nil,
				"captured":        true,
				"description":     nil,
				"card_id":         nil,
				"bank":            nil,
				"wallet":          nil,
				"vpa":             "success@razorpay",
				"email":           "[email protected]",
				"contact":         "+919999999999",
				"notes": map[string]interface{}{
					"notes_key_1": "Tea, Earl Grey, Hot",
					"notes_key_2": "Tea, Earl Grey… decaf.",
				},
				"fee":               float64(2),
				"tax":               float64(0),
				"error_code":        nil,
				"error_description": nil,
				"error_source":      nil,
				"error_step":        nil,
				"error_reason":      nil,
				"acquirer_data": map[string]interface{}{
					"rrn":                "267567962619",
					"upi_transaction_id": "F5B66C7C07CA6FEAD77E956DC2FC7ABE",
				},
				"created_at": float64(1701688721),
				"upi": map[string]interface{}{
					"vpa": "success@razorpay",
				},
			},
		},
	}

	orderNotFoundResp := map[string]interface{}{
		"error": map[string]interface{}{
			"code":        "BAD_REQUEST_ERROR",
			"description": "order not found",
		},
	}

	tests := []RazorpayToolTestCase{
		{
			Name: "successful fetch of order payments",
			Request: map[string]interface{}{
				"order_id": "order_N8FRN5zTm5S3wx",
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path: fmt.Sprintf(
							fetchOrderPaymentsPathFmt,
							"order_N8FRN5zTm5S3wx",
						),
						Method:   "GET",
						Response: paymentsResp,
					},
				)
			},
			ExpectError:    false,
			ExpectedResult: paymentsResp,
		},
		{
			Name: "order not found",
			Request: map[string]interface{}{
				"order_id": "order_invalid",
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path: fmt.Sprintf(
							fetchOrderPaymentsPathFmt,
							"order_invalid",
						),
						Method:   "GET",
						Response: orderNotFoundResp,
					},
				)
			},
			ExpectError:    true,
			ExpectedErrMsg: "fetching payments for order failed: order not found",
		},
		{
			Name:           "missing order_id parameter",
			Request:        map[string]interface{}{},
			MockHttpClient: nil, // No HTTP client needed for validation error
			ExpectError:    true,
			ExpectedErrMsg: "missing required parameter: order_id",
		},
	}

	for _, tc := range tests {
		t.Run(tc.Name, func(t *testing.T) {
			runToolTest(t, tc, FetchOrderPayments, "Order")
		})
	}
}

func Test_UpdateOrder(t *testing.T) {
	updateOrderPathFmt := fmt.Sprintf(
		"/%s%s/%%s",
		constants.VERSION_V1,
		constants.ORDER_URL,
	)

	updatedOrderResp := map[string]interface{}{
		"id":         "order_EKwxwAgItmmXdp",
		"entity":     "order",
		"amount":     float64(10000),
		"currency":   "INR",
		"receipt":    "receipt-123",
		"status":     "created",
		"attempts":   float64(0),
		"created_at": float64(1572505143),
		"notes": map[string]interface{}{
			"customer_name": "updated-customer",
			"product_name":  "updated-product",
		},
	}

	orderNotFoundResp := map[string]interface{}{
		"error": map[string]interface{}{
			"code":        "BAD_REQUEST_ERROR",
			"description": "order not found",
		},
	}

	tests := []RazorpayToolTestCase{
		{
			Name: "successful order update",
			Request: map[string]interface{}{
				"order_id": "order_EKwxwAgItmmXdp",
				"notes": map[string]interface{}{
					"customer_name": "updated-customer",
					"product_name":  "updated-product",
				},
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path: fmt.Sprintf(
							updateOrderPathFmt, "order_EKwxwAgItmmXdp"),
						Method:   "PATCH",
						Response: updatedOrderResp,
					},
				)
			},
			ExpectError:    false,
			ExpectedResult: updatedOrderResp,
		},
		{
			Name: "missing required parameters - order_id",
			Request: map[string]interface{}{
				// Missing order_id
				"notes": map[string]interface{}{
					"customer_name": "updated-customer",
					"product_name":  "updated-product",
				},
			},
			MockHttpClient: nil, // No HTTP client needed for validation error
			ExpectError:    true,
			ExpectedErrMsg: "missing required parameter: order_id",
		},
		{
			Name: "missing required parameters - notes",
			Request: map[string]interface{}{
				"order_id": "order_EKwxwAgItmmXdp",
				// Missing notes
			},
			MockHttpClient: nil, // No HTTP client needed for validation error
			ExpectError:    true,
			ExpectedErrMsg: "missing required parameter: notes",
		},
		{
			Name: "order not found",
			Request: map[string]interface{}{
				"order_id": "order_invalid_id",
				"notes": map[string]interface{}{
					"customer_name": "updated-customer",
					"product_name":  "updated-product",
				},
			},
			MockHttpClient: func() (*http.Client, *httptest.Server) {
				return mock.NewHTTPClient(
					mock.Endpoint{
						Path:     fmt.Sprintf(updateOrderPathFmt, "order_invalid_id"),
						Method:   "PATCH",
						Response: orderNotFoundResp,
					},
				)
			},
			ExpectError:    true,
			ExpectedErrMsg: "updating order failed: order not found",
		},
	}

	for _, tc := range tests {
		t.Run(tc.Name, func(t *testing.T) {
			runToolTest(t, tc, UpdateOrder, "Order")
		})
	}
}

```
Page 2/4FirstPrevNextLast