#
tokens: 48065/50000 1/59 files (page 5/5)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 5 of 5. Use http://codebase.md/razorpay/razorpay-mcp-server?lines=true&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/razorpay/payments_test.go:
--------------------------------------------------------------------------------

```go
   1 | package razorpay
   2 | 
   3 | import (
   4 | 	"context"
   5 | 	"fmt"
   6 | 	"net/http"
   7 | 	"net/http/httptest"
   8 | 	"strings"
   9 | 	"testing"
  10 | 
  11 | 	"github.com/razorpay/razorpay-go/constants"
  12 | 
  13 | 	"github.com/razorpay/razorpay-mcp-server/pkg/mcpgo"
  14 | 	"github.com/razorpay/razorpay-mcp-server/pkg/razorpay/mock"
  15 | )
  16 | 
  17 | func Test_FetchPayment(t *testing.T) {
  18 | 	fetchPaymentPathFmt := fmt.Sprintf(
  19 | 		"/%s%s/%%s",
  20 | 		constants.VERSION_V1,
  21 | 		constants.PAYMENT_URL,
  22 | 	)
  23 | 
  24 | 	paymentResp := map[string]interface{}{
  25 | 		"id":     "pay_MT48CvBhIC98MQ",
  26 | 		"amount": float64(1000),
  27 | 		"status": "captured",
  28 | 	}
  29 | 
  30 | 	paymentNotFoundResp := map[string]interface{}{
  31 | 		"error": map[string]interface{}{
  32 | 			"code":        "BAD_REQUEST_ERROR",
  33 | 			"description": "payment not found",
  34 | 		},
  35 | 	}
  36 | 
  37 | 	tests := []RazorpayToolTestCase{
  38 | 		{
  39 | 			Name: "successful payment fetch",
  40 | 			Request: map[string]interface{}{
  41 | 				"payment_id": "pay_MT48CvBhIC98MQ",
  42 | 			},
  43 | 			MockHttpClient: func() (*http.Client, *httptest.Server) {
  44 | 				return mock.NewHTTPClient(
  45 | 					mock.Endpoint{
  46 | 						Path:     fmt.Sprintf(fetchPaymentPathFmt, "pay_MT48CvBhIC98MQ"),
  47 | 						Method:   "GET",
  48 | 						Response: paymentResp,
  49 | 					},
  50 | 				)
  51 | 			},
  52 | 			ExpectError:    false,
  53 | 			ExpectedResult: paymentResp,
  54 | 		},
  55 | 		{
  56 | 			Name: "payment not found",
  57 | 			Request: map[string]interface{}{
  58 | 				"payment_id": "pay_invalid",
  59 | 			},
  60 | 			MockHttpClient: func() (*http.Client, *httptest.Server) {
  61 | 				return mock.NewHTTPClient(
  62 | 					mock.Endpoint{
  63 | 						Path:     fmt.Sprintf(fetchPaymentPathFmt, "pay_invalid"),
  64 | 						Method:   "GET",
  65 | 						Response: paymentNotFoundResp,
  66 | 					},
  67 | 				)
  68 | 			},
  69 | 			ExpectError:    true,
  70 | 			ExpectedErrMsg: "fetching payment failed: payment not found",
  71 | 		},
  72 | 		{
  73 | 			Name:           "missing payment_id parameter",
  74 | 			Request:        map[string]interface{}{},
  75 | 			MockHttpClient: nil, // No HTTP client needed for validation error
  76 | 			ExpectError:    true,
  77 | 			ExpectedErrMsg: "missing required parameter: payment_id",
  78 | 		},
  79 | 	}
  80 | 
  81 | 	for _, tc := range tests {
  82 | 		t.Run(tc.Name, func(t *testing.T) {
  83 | 			runToolTest(t, tc, FetchPayment, "Payment")
  84 | 		})
  85 | 	}
  86 | }
  87 | 
  88 | func Test_FetchPaymentCardDetails(t *testing.T) {
  89 | 	fetchCardDetailsPathFmt := fmt.Sprintf(
  90 | 		"/%s%s/%%s/card",
  91 | 		constants.VERSION_V1,
  92 | 		constants.PAYMENT_URL,
  93 | 	)
  94 | 
  95 | 	cardDetailsResp := map[string]interface{}{
  96 | 		"id":            "card_JXPULjlKqC5j0i",
  97 | 		"entity":        "card",
  98 | 		"name":          "Gaurav Kumar",
  99 | 		"last4":         "4366",
 100 | 		"network":       "Visa",
 101 | 		"type":          "credit",
 102 | 		"issuer":        "UTIB",
 103 | 		"international": false,
 104 | 		"emi":           false,
 105 | 		"sub_type":      "consumer",
 106 | 		"token_iin":     nil,
 107 | 	}
 108 | 
 109 | 	paymentNotFoundResp := map[string]interface{}{
 110 | 		"error": map[string]interface{}{
 111 | 			"code":        "BAD_REQUEST_ERROR",
 112 | 			"description": "The id provided does not exist",
 113 | 		},
 114 | 	}
 115 | 
 116 | 	tests := []RazorpayToolTestCase{
 117 | 		{
 118 | 			Name: "successful card details fetch",
 119 | 			Request: map[string]interface{}{
 120 | 				"payment_id": "pay_DtFYPi3IfUTgsL",
 121 | 			},
 122 | 			MockHttpClient: func() (*http.Client, *httptest.Server) {
 123 | 				return mock.NewHTTPClient(
 124 | 					mock.Endpoint{
 125 | 						Path:     fmt.Sprintf(fetchCardDetailsPathFmt, "pay_DtFYPi3IfUTgsL"),
 126 | 						Method:   "GET",
 127 | 						Response: cardDetailsResp,
 128 | 					},
 129 | 				)
 130 | 			},
 131 | 			ExpectError:    false,
 132 | 			ExpectedResult: cardDetailsResp,
 133 | 		},
 134 | 		{
 135 | 			Name: "payment not found",
 136 | 			Request: map[string]interface{}{
 137 | 				"payment_id": "pay_invalid",
 138 | 			},
 139 | 			MockHttpClient: func() (*http.Client, *httptest.Server) {
 140 | 				return mock.NewHTTPClient(
 141 | 					mock.Endpoint{
 142 | 						Path:     fmt.Sprintf(fetchCardDetailsPathFmt, "pay_invalid"),
 143 | 						Method:   "GET",
 144 | 						Response: paymentNotFoundResp,
 145 | 					},
 146 | 				)
 147 | 			},
 148 | 			ExpectError: true,
 149 | 			ExpectedErrMsg: "fetching card details failed: " +
 150 | 				"The id provided does not exist",
 151 | 		},
 152 | 		{
 153 | 			Name:           "missing payment_id parameter",
 154 | 			Request:        map[string]interface{}{},
 155 | 			MockHttpClient: nil, // No HTTP client needed for validation error
 156 | 			ExpectError:    true,
 157 | 			ExpectedErrMsg: "missing required parameter: payment_id",
 158 | 		},
 159 | 	}
 160 | 
 161 | 	for _, tc := range tests {
 162 | 		t.Run(tc.Name, func(t *testing.T) {
 163 | 			runToolTest(t, tc, FetchPaymentCardDetails, "Card Details")
 164 | 		})
 165 | 	}
 166 | }
 167 | 
 168 | func Test_CapturePayment(t *testing.T) {
 169 | 	capturePaymentPathFmt := fmt.Sprintf(
 170 | 		"/%s%s/%%s/capture",
 171 | 		constants.VERSION_V1,
 172 | 		constants.PAYMENT_URL,
 173 | 	)
 174 | 
 175 | 	successfulCaptureResp := map[string]interface{}{
 176 | 		"id":                "pay_G3P9vcIhRs3NV4",
 177 | 		"entity":            "payment",
 178 | 		"amount":            float64(1000),
 179 | 		"currency":          "INR",
 180 | 		"status":            "captured",
 181 | 		"order_id":          "order_GjCr5oKh4AVC51",
 182 | 		"invoice_id":        nil,
 183 | 		"international":     false,
 184 | 		"method":            "card",
 185 | 		"amount_refunded":   float64(0),
 186 | 		"refund_status":     nil,
 187 | 		"captured":          true,
 188 | 		"description":       "Payment for Adidas shoes",
 189 | 		"card_id":           "card_KOdY30ajbuyOYN",
 190 | 		"bank":              nil,
 191 | 		"wallet":            nil,
 192 | 		"vpa":               nil,
 193 | 		"email":             "[email protected]",
 194 | 		"contact":           "9000090000",
 195 | 		"customer_id":       "cust_K6fNE0WJZWGqtN",
 196 | 		"token_id":          "token_KOdY$DBYQOv08n",
 197 | 		"notes":             []interface{}{},
 198 | 		"fee":               float64(1),
 199 | 		"tax":               float64(0),
 200 | 		"error_code":        nil,
 201 | 		"error_description": nil,
 202 | 		"error_source":      nil,
 203 | 		"error_step":        nil,
 204 | 		"error_reason":      nil,
 205 | 		"acquirer_data": map[string]interface{}{
 206 | 			"authentication_reference_number": "100222021120200000000742753928",
 207 | 		},
 208 | 		"created_at": float64(1605871409),
 209 | 	}
 210 | 
 211 | 	alreadyCapturedResp := map[string]interface{}{
 212 | 		"error": map[string]interface{}{
 213 | 			"code":        "BAD_REQUEST_ERROR",
 214 | 			"description": "This payment has already been captured",
 215 | 		},
 216 | 	}
 217 | 
 218 | 	tests := []RazorpayToolTestCase{
 219 | 		{
 220 | 			Name: "successful payment capture",
 221 | 			Request: map[string]interface{}{
 222 | 				"payment_id": "pay_G3P9vcIhRs3NV4",
 223 | 				"amount":     float64(1000),
 224 | 				"currency":   "INR",
 225 | 			},
 226 | 			MockHttpClient: func() (*http.Client, *httptest.Server) {
 227 | 				return mock.NewHTTPClient(
 228 | 					mock.Endpoint{
 229 | 						Path:     fmt.Sprintf(capturePaymentPathFmt, "pay_G3P9vcIhRs3NV4"),
 230 | 						Method:   "POST",
 231 | 						Response: successfulCaptureResp,
 232 | 					},
 233 | 				)
 234 | 			},
 235 | 			ExpectError:    false,
 236 | 			ExpectedResult: successfulCaptureResp,
 237 | 		},
 238 | 		{
 239 | 			Name: "payment already captured",
 240 | 			Request: map[string]interface{}{
 241 | 				"payment_id": "pay_G3P9vcIhRs3NV4",
 242 | 				"amount":     float64(1000),
 243 | 				"currency":   "INR",
 244 | 			},
 245 | 			MockHttpClient: func() (*http.Client, *httptest.Server) {
 246 | 				return mock.NewHTTPClient(
 247 | 					mock.Endpoint{
 248 | 						Path:     fmt.Sprintf(capturePaymentPathFmt, "pay_G3P9vcIhRs3NV4"),
 249 | 						Method:   "POST",
 250 | 						Response: alreadyCapturedResp,
 251 | 					},
 252 | 				)
 253 | 			},
 254 | 			ExpectError: true,
 255 | 			ExpectedErrMsg: "capturing payment failed: This payment has already been " +
 256 | 				"captured",
 257 | 		},
 258 | 		{
 259 | 			Name: "missing payment_id parameter",
 260 | 			Request: map[string]interface{}{
 261 | 				"amount":   float64(1000),
 262 | 				"currency": "INR",
 263 | 			},
 264 | 			MockHttpClient: nil, // No HTTP client needed for validation error
 265 | 			ExpectError:    true,
 266 | 			ExpectedErrMsg: "missing required parameter: payment_id",
 267 | 		},
 268 | 		{
 269 | 			Name: "missing amount parameter",
 270 | 			Request: map[string]interface{}{
 271 | 				"payment_id": "pay_G3P9vcIhRs3NV4",
 272 | 				"currency":   "INR",
 273 | 			},
 274 | 			MockHttpClient: nil, // No HTTP client needed for validation error
 275 | 			ExpectError:    true,
 276 | 			ExpectedErrMsg: "missing required parameter: amount",
 277 | 		},
 278 | 		{
 279 | 			Name: "missing currency parameter",
 280 | 			Request: map[string]interface{}{
 281 | 				"payment_id": "pay_G3P9vcIhRs3NV4",
 282 | 				"amount":     float64(1000),
 283 | 			},
 284 | 			MockHttpClient: nil, // No HTTP client needed for validation error
 285 | 			ExpectError:    true,
 286 | 			ExpectedErrMsg: "missing required parameter: currency",
 287 | 		},
 288 | 		{
 289 | 			Name:    "multiple validation errors",
 290 | 			Request: map[string]interface{}{
 291 | 				// All required parameters missing
 292 | 			},
 293 | 			MockHttpClient: nil, // No HTTP client needed for validation error
 294 | 			ExpectError:    true,
 295 | 			ExpectedErrMsg: "Validation errors:\n- " +
 296 | 				"missing required parameter: payment_id\n- " +
 297 | 				"missing required parameter: amount\n- " +
 298 | 				"missing required parameter: currency",
 299 | 		},
 300 | 	}
 301 | 
 302 | 	for _, tc := range tests {
 303 | 		t.Run(tc.Name, func(t *testing.T) {
 304 | 			runToolTest(t, tc, CapturePayment, "Payment")
 305 | 		})
 306 | 	}
 307 | }
 308 | 
 309 | func Test_UpdatePayment(t *testing.T) {
 310 | 	updatePaymentPathFmt := fmt.Sprintf(
 311 | 		"/%s%s/%%s",
 312 | 		constants.VERSION_V1,
 313 | 		constants.PAYMENT_URL,
 314 | 	)
 315 | 
 316 | 	successfulUpdateResp := map[string]interface{}{
 317 | 		"id":              "pay_KbCVlLqUbb3VhA",
 318 | 		"entity":          "payment",
 319 | 		"amount":          float64(400000),
 320 | 		"currency":        "INR",
 321 | 		"status":          "authorized",
 322 | 		"order_id":        nil,
 323 | 		"invoice_id":      nil,
 324 | 		"international":   false,
 325 | 		"method":          "emi",
 326 | 		"amount_refunded": float64(0),
 327 | 		"refund_status":   nil,
 328 | 		"captured":        false,
 329 | 		"description":     "Test Transaction",
 330 | 		"card_id":         "card_KbCVlPnxWRlOpH",
 331 | 		"bank":            "HDFC",
 332 | 		"wallet":          nil,
 333 | 		"vpa":             nil,
 334 | 		"email":           "[email protected]",
 335 | 		"contact":         "+919000090000",
 336 | 		"notes": map[string]interface{}{
 337 | 			"key1": "value1",
 338 | 			"key2": "value2",
 339 | 		},
 340 | 		"fee":               nil,
 341 | 		"tax":               nil,
 342 | 		"error_code":        nil,
 343 | 		"error_description": nil,
 344 | 		"error_source":      nil,
 345 | 		"error_step":        nil,
 346 | 		"error_reason":      nil,
 347 | 		"acquirer_data": map[string]interface{}{
 348 | 			"auth_code": "205480",
 349 | 		},
 350 | 		"emi_plan": map[string]interface{}{
 351 | 			"issuer":   "HDFC",
 352 | 			"type":     "credit",
 353 | 			"rate":     float64(1500),
 354 | 			"duration": float64(24),
 355 | 		},
 356 | 		"created_at": float64(1667398779),
 357 | 	}
 358 | 
 359 | 	paymentNotFoundResp := map[string]interface{}{
 360 | 		"error": map[string]interface{}{
 361 | 			"code":        "BAD_REQUEST_ERROR",
 362 | 			"description": "The id provided does not exist",
 363 | 		},
 364 | 	}
 365 | 
 366 | 	tests := []RazorpayToolTestCase{
 367 | 		{
 368 | 			Name: "successful payment notes update",
 369 | 			Request: map[string]interface{}{
 370 | 				"payment_id": "pay_KbCVlLqUbb3VhA",
 371 | 				"notes": map[string]interface{}{
 372 | 					"key1": "value1",
 373 | 					"key2": "value2",
 374 | 				},
 375 | 			},
 376 | 			MockHttpClient: func() (*http.Client, *httptest.Server) {
 377 | 				return mock.NewHTTPClient(
 378 | 					mock.Endpoint{
 379 | 						Path:     fmt.Sprintf(updatePaymentPathFmt, "pay_KbCVlLqUbb3VhA"),
 380 | 						Method:   "PATCH",
 381 | 						Response: successfulUpdateResp,
 382 | 					},
 383 | 				)
 384 | 			},
 385 | 			ExpectError:    false,
 386 | 			ExpectedResult: successfulUpdateResp,
 387 | 		},
 388 | 		{
 389 | 			Name: "payment not found",
 390 | 			Request: map[string]interface{}{
 391 | 				"payment_id": "pay_invalid",
 392 | 				"notes": map[string]interface{}{
 393 | 					"key1": "value1",
 394 | 				},
 395 | 			},
 396 | 			MockHttpClient: func() (*http.Client, *httptest.Server) {
 397 | 				return mock.NewHTTPClient(
 398 | 					mock.Endpoint{
 399 | 						Path:     fmt.Sprintf(updatePaymentPathFmt, "pay_invalid"),
 400 | 						Method:   "PATCH",
 401 | 						Response: paymentNotFoundResp,
 402 | 					},
 403 | 				)
 404 | 			},
 405 | 			ExpectError:    true,
 406 | 			ExpectedErrMsg: "updating payment failed: The id provided does not exist",
 407 | 		},
 408 | 		{
 409 | 			Name: "missing payment_id parameter",
 410 | 			Request: map[string]interface{}{
 411 | 				"notes": map[string]interface{}{
 412 | 					"key1": "value1",
 413 | 				},
 414 | 			},
 415 | 			MockHttpClient: nil, // No HTTP client needed for validation error
 416 | 			ExpectError:    true,
 417 | 			ExpectedErrMsg: "missing required parameter: payment_id",
 418 | 		},
 419 | 		{
 420 | 			Name: "missing notes parameter",
 421 | 			Request: map[string]interface{}{
 422 | 				"payment_id": "pay_KbCVlLqUbb3VhA",
 423 | 			},
 424 | 			MockHttpClient: nil, // No HTTP client needed for validation error
 425 | 			ExpectError:    true,
 426 | 			ExpectedErrMsg: "missing required parameter: notes",
 427 | 		},
 428 | 		{
 429 | 			Name:    "multiple validation errors",
 430 | 			Request: map[string]interface{}{
 431 | 				// All required parameters missing
 432 | 			},
 433 | 			MockHttpClient: nil, // No HTTP client needed for validation error
 434 | 			ExpectError:    true,
 435 | 			ExpectedErrMsg: "Validation errors:\n- " +
 436 | 				"missing required parameter: payment_id\n- " +
 437 | 				"missing required parameter: notes",
 438 | 		},
 439 | 	}
 440 | 
 441 | 	for _, tc := range tests {
 442 | 		t.Run(tc.Name, func(t *testing.T) {
 443 | 			runToolTest(t, tc, UpdatePayment, "Payment")
 444 | 		})
 445 | 	}
 446 | }
 447 | 
 448 | func Test_FetchAllPayments(t *testing.T) {
 449 | 	fetchAllPaymentsPath := fmt.Sprintf(
 450 | 		"/%s%s",
 451 | 		constants.VERSION_V1,
 452 | 		constants.PAYMENT_URL,
 453 | 	)
 454 | 
 455 | 	// Sample response for successful fetch
 456 | 	paymentsListResp := map[string]interface{}{
 457 | 		"entity": "collection",
 458 | 		"count":  float64(2),
 459 | 		"items": []interface{}{
 460 | 			map[string]interface{}{
 461 | 				"id":              "pay_KbCFyQ0t9Lmi1n",
 462 | 				"entity":          "payment",
 463 | 				"amount":          float64(1000),
 464 | 				"currency":        "INR",
 465 | 				"status":          "authorized",
 466 | 				"order_id":        nil,
 467 | 				"invoice_id":      nil,
 468 | 				"international":   false,
 469 | 				"method":          "netbanking",
 470 | 				"amount_refunded": float64(0),
 471 | 				"refund_status":   nil,
 472 | 				"captured":        false,
 473 | 				"description":     "Test Transaction",
 474 | 				"card_id":         nil,
 475 | 				"bank":            "IBKL",
 476 | 				"wallet":          nil,
 477 | 				"vpa":             nil,
 478 | 				"email":           "[email protected]",
 479 | 				"contact":         "+919000090000",
 480 | 				"notes": map[string]interface{}{
 481 | 					"address": "Razorpay Corporate Office",
 482 | 				},
 483 | 				"fee":               nil,
 484 | 				"tax":               nil,
 485 | 				"error_code":        nil,
 486 | 				"error_description": nil,
 487 | 				"error_source":      nil,
 488 | 				"error_step":        nil,
 489 | 				"error_reason":      nil,
 490 | 				"acquirer_data": map[string]interface{}{
 491 | 					"bank_transaction_id": "5733649",
 492 | 				},
 493 | 				"created_at": float64(1667397881),
 494 | 			},
 495 | 			map[string]interface{}{
 496 | 				"id":              "pay_KbCEDHh1IrU4RJ",
 497 | 				"entity":          "payment",
 498 | 				"amount":          float64(1000),
 499 | 				"currency":        "INR",
 500 | 				"status":          "authorized",
 501 | 				"order_id":        nil,
 502 | 				"invoice_id":      nil,
 503 | 				"international":   false,
 504 | 				"method":          "upi",
 505 | 				"amount_refunded": float64(0),
 506 | 				"refund_status":   nil,
 507 | 				"captured":        false,
 508 | 				"description":     "Test Transaction",
 509 | 				"card_id":         nil,
 510 | 				"bank":            nil,
 511 | 				"wallet":          nil,
 512 | 				"vpa":             "gaurav.kumar@okhdfcbank",
 513 | 				"email":           "[email protected]",
 514 | 				"contact":         "+919000090000",
 515 | 				"notes": map[string]interface{}{
 516 | 					"address": "Razorpay Corporate Office",
 517 | 				},
 518 | 				"fee":               nil,
 519 | 				"tax":               nil,
 520 | 				"error_code":        nil,
 521 | 				"error_description": nil,
 522 | 				"error_source":      nil,
 523 | 				"error_step":        nil,
 524 | 				"error_reason":      nil,
 525 | 				"acquirer_data": map[string]interface{}{
 526 | 					"rrn":                "230901495295",
 527 | 					"upi_transaction_id": "6935B87A72C2A7BC83FA927AA264AD53",
 528 | 				},
 529 | 				"created_at": float64(1667397781),
 530 | 			},
 531 | 		},
 532 | 	}
 533 | 
 534 | 	// Error response when parameters are invalid
 535 | 	invalidParamsResp := map[string]interface{}{
 536 | 		"error": map[string]interface{}{
 537 | 			"code":        "BAD_REQUEST_ERROR",
 538 | 			"description": "from must be between 946684800 and 4765046400",
 539 | 		},
 540 | 	}
 541 | 
 542 | 	tests := []RazorpayToolTestCase{
 543 | 		{
 544 | 			Name: "successful payments fetch with all parameters",
 545 | 			Request: map[string]interface{}{
 546 | 				"from":  float64(1593320020),
 547 | 				"to":    float64(1624856020),
 548 | 				"count": float64(2),
 549 | 				"skip":  float64(1),
 550 | 			},
 551 | 			MockHttpClient: func() (*http.Client, *httptest.Server) {
 552 | 				return mock.NewHTTPClient(
 553 | 					mock.Endpoint{
 554 | 						Path:     fetchAllPaymentsPath,
 555 | 						Method:   "GET",
 556 | 						Response: paymentsListResp,
 557 | 					},
 558 | 				)
 559 | 			},
 560 | 			ExpectError:    false,
 561 | 			ExpectedResult: paymentsListResp,
 562 | 		},
 563 | 		{
 564 | 			Name: "payments fetch with invalid timestamp",
 565 | 			Request: map[string]interface{}{
 566 | 				"from": float64(900000000), // Invalid timestamp (too early)
 567 | 				"to":   float64(1624856020),
 568 | 			},
 569 | 			MockHttpClient: func() (*http.Client, *httptest.Server) {
 570 | 				return mock.NewHTTPClient(
 571 | 					mock.Endpoint{
 572 | 						Path:     fetchAllPaymentsPath,
 573 | 						Method:   "GET",
 574 | 						Response: invalidParamsResp,
 575 | 					},
 576 | 				)
 577 | 			},
 578 | 			ExpectError: true,
 579 | 			ExpectedErrMsg: "fetching payments failed: from must be between " +
 580 | 				"946684800 and 4765046400",
 581 | 		},
 582 | 		{
 583 | 			Name: "multiple validation errors with wrong types",
 584 | 			Request: map[string]interface{}{
 585 | 				"count": "not_a_number",
 586 | 				"skip":  "not_a_number",
 587 | 				"from":  "not_a_number",
 588 | 				"to":    "not_a_number",
 589 | 			},
 590 | 			MockHttpClient: nil, // No HTTP client needed for validation error
 591 | 			ExpectError:    true,
 592 | 			ExpectedErrMsg: "Validation errors:\n- " +
 593 | 				"invalid parameter type: count\n- " +
 594 | 				"invalid parameter type: skip\n- " +
 595 | 				"invalid parameter type: from\n- " +
 596 | 				"invalid parameter type: to",
 597 | 		},
 598 | 	}
 599 | 
 600 | 	for _, tc := range tests {
 601 | 		t.Run(tc.Name, func(t *testing.T) {
 602 | 			runToolTest(t, tc, FetchAllPayments, "Payments List")
 603 | 		})
 604 | 	}
 605 | }
 606 | 
 607 | func Test_InitiatePayment(t *testing.T) {
 608 | 	initiatePaymentPath := fmt.Sprintf(
 609 | 		"/%s%s/create/json",
 610 | 		constants.VERSION_V1,
 611 | 		constants.PAYMENT_URL,
 612 | 	)
 613 | 
 614 | 	createCustomerPath := fmt.Sprintf(
 615 | 		"/%s%s",
 616 | 		constants.VERSION_V1,
 617 | 		constants.CUSTOMER_URL,
 618 | 	)
 619 | 
 620 | 	customerResp := map[string]interface{}{
 621 | 		"id":         "cust_1Aa00000000003",
 622 | 		"entity":     "customer",
 623 | 		"name":       "",
 624 | 		"email":      "",
 625 | 		"contact":    "9876543210",
 626 | 		"gstin":      nil,
 627 | 		"notes":      map[string]interface{}{},
 628 | 		"created_at": float64(1234567890),
 629 | 	}
 630 | 
 631 | 	successPaymentWithRedirectResp := map[string]interface{}{
 632 | 		"razorpay_payment_id": "pay_MT48CvBhIC98MQ",
 633 | 		"status":              "created",
 634 | 		"amount":              float64(10000),
 635 | 		"currency":            "INR",
 636 | 		"order_id":            "order_129837127313912",
 637 | 		"next": []interface{}{
 638 | 			map[string]interface{}{
 639 | 				"action": "redirect",
 640 | 				"url": "https://api.razorpay.com/v1/payments/" +
 641 | 					"pay_MT48CvBhIC98MQ/authenticate",
 642 | 			},
 643 | 		},
 644 | 	}
 645 | 
 646 | 	successPaymentWithoutNextResp := map[string]interface{}{
 647 | 		"razorpay_payment_id": "pay_MT48CvBhIC98MQ",
 648 | 		"status":              "captured",
 649 | 		"amount":              float64(10000),
 650 | 		"currency":            "INR",
 651 | 		"order_id":            "order_129837127313912",
 652 | 	}
 653 | 
 654 | 	paymentErrorResp := map[string]interface{}{
 655 | 		"error": map[string]interface{}{
 656 | 			"code":        "BAD_REQUEST_ERROR",
 657 | 			"description": "Invalid token",
 658 | 		},
 659 | 	}
 660 | 
 661 | 	tests := []RazorpayToolTestCase{
 662 | 		{
 663 | 			Name: "successful payment initiation without next actions",
 664 | 			Request: map[string]interface{}{
 665 | 				"amount":   10000,
 666 | 				"currency": "INR",
 667 | 				"token":    "token_MT48CvBhIC98MQ",
 668 | 				"order_id": "order_129837127313912",
 669 | 				"email":    "[email protected]",
 670 | 				"contact":  "9876543210",
 671 | 			},
 672 | 			MockHttpClient: func() (*http.Client, *httptest.Server) {
 673 | 				return mock.NewHTTPClient(
 674 | 					mock.Endpoint{
 675 | 						Path:     createCustomerPath,
 676 | 						Method:   "POST",
 677 | 						Response: customerResp,
 678 | 					},
 679 | 					mock.Endpoint{
 680 | 						Path:     initiatePaymentPath,
 681 | 						Method:   "POST",
 682 | 						Response: successPaymentWithoutNextResp,
 683 | 					},
 684 | 				)
 685 | 			},
 686 | 			ExpectError: false,
 687 | 			ExpectedResult: map[string]interface{}{
 688 | 				"razorpay_payment_id": "pay_MT48CvBhIC98MQ",
 689 | 				"payment_details":     successPaymentWithoutNextResp,
 690 | 				"status":              "payment_initiated",
 691 | 				"message": "Payment initiated successfully using " +
 692 | 					"S2S JSON v1 flow",
 693 | 				"next_step": "Use 'resend_otp' to regenerate OTP or " +
 694 | 					"'submit_otp' to proceed to enter OTP if " +
 695 | 					"OTP authentication is required.",
 696 | 				"next_tool": "resend_otp",
 697 | 				"next_tool_params": map[string]interface{}{
 698 | 					"payment_id": "pay_MT48CvBhIC98MQ",
 699 | 				},
 700 | 			},
 701 | 		},
 702 | 		{
 703 | 			Name: "successful payment initiation with redirect",
 704 | 			Request: map[string]interface{}{
 705 | 				"amount":   10000,
 706 | 				"token":    "token_MT48CvBhIC98MQ",
 707 | 				"order_id": "order_129837127313912",
 708 | 			},
 709 | 			MockHttpClient: func() (*http.Client, *httptest.Server) {
 710 | 				return mock.NewHTTPClient(
 711 | 					mock.Endpoint{
 712 | 						Path:     initiatePaymentPath,
 713 | 						Method:   "POST",
 714 | 						Response: successPaymentWithRedirectResp,
 715 | 					},
 716 | 				)
 717 | 			},
 718 | 			ExpectError: false,
 719 | 			ExpectedResult: map[string]interface{}{
 720 | 				"razorpay_payment_id": "pay_MT48CvBhIC98MQ",
 721 | 				"payment_details":     successPaymentWithRedirectResp,
 722 | 				"status":              "payment_initiated",
 723 | 				"message": "Payment initiated. Redirect authentication is available. " +
 724 | 					"Use the redirect URL provided in available_actions.",
 725 | 				"available_actions": []interface{}{
 726 | 					map[string]interface{}{
 727 | 						"action": "redirect",
 728 | 						"url": "https://api.razorpay.com/v1/payments/" +
 729 | 							"pay_MT48CvBhIC98MQ/authenticate",
 730 | 					},
 731 | 				},
 732 | 			},
 733 | 		},
 734 | 		{
 735 | 			Name: "successful payment initiation with contact only",
 736 | 			Request: map[string]interface{}{
 737 | 				"amount":   10000,
 738 | 				"token":    "token_MT48CvBhIC98MQ",
 739 | 				"order_id": "order_129837127313912",
 740 | 				"contact":  "9876543210",
 741 | 			},
 742 | 			MockHttpClient: func() (*http.Client, *httptest.Server) {
 743 | 				return mock.NewHTTPClient(
 744 | 					mock.Endpoint{
 745 | 						Path:     createCustomerPath,
 746 | 						Method:   "POST",
 747 | 						Response: customerResp,
 748 | 					},
 749 | 					mock.Endpoint{
 750 | 						Path:     initiatePaymentPath,
 751 | 						Method:   "POST",
 752 | 						Response: successPaymentWithoutNextResp,
 753 | 					},
 754 | 				)
 755 | 			},
 756 | 			ExpectError: false,
 757 | 			ExpectedResult: map[string]interface{}{
 758 | 				"razorpay_payment_id": "pay_MT48CvBhIC98MQ",
 759 | 				"payment_details":     successPaymentWithoutNextResp,
 760 | 				"status":              "payment_initiated",
 761 | 				"message": "Payment initiated successfully using " +
 762 | 					"S2S JSON v1 flow",
 763 | 				"next_step": "Use 'resend_otp' to regenerate OTP or " +
 764 | 					"'submit_otp' to proceed to enter OTP if " +
 765 | 					"OTP authentication is required.",
 766 | 				"next_tool": "resend_otp",
 767 | 				"next_tool_params": map[string]interface{}{
 768 | 					"payment_id": "pay_MT48CvBhIC98MQ",
 769 | 				},
 770 | 			},
 771 | 		},
 772 | 		{
 773 | 			Name: "payment initiation with API error",
 774 | 			Request: map[string]interface{}{
 775 | 				"amount":   10000,
 776 | 				"token":    "token_invalid",
 777 | 				"order_id": "order_129837127313912",
 778 | 			},
 779 | 			MockHttpClient: func() (*http.Client, *httptest.Server) {
 780 | 				return mock.NewHTTPClient(
 781 | 					mock.Endpoint{
 782 | 						Path:     initiatePaymentPath,
 783 | 						Method:   "POST",
 784 | 						Response: paymentErrorResp,
 785 | 					},
 786 | 				)
 787 | 			},
 788 | 			ExpectError:    true,
 789 | 			ExpectedErrMsg: "initiating payment failed:",
 790 | 		},
 791 | 		{
 792 | 			Name: "missing required amount parameter",
 793 | 			Request: map[string]interface{}{
 794 | 				"token":    "token_MT48CvBhIC98MQ",
 795 | 				"order_id": "order_129837127313912",
 796 | 			},
 797 | 			MockHttpClient: nil,
 798 | 			ExpectError:    true,
 799 | 			ExpectedErrMsg: "missing required parameter: amount",
 800 | 		},
 801 | 		{
 802 | 			Name: "missing required order_id parameter",
 803 | 			Request: map[string]interface{}{
 804 | 				"amount": 10000,
 805 | 				"token":  "token_MT48CvBhIC98MQ",
 806 | 			},
 807 | 			MockHttpClient: nil,
 808 | 			ExpectError:    true,
 809 | 			ExpectedErrMsg: "missing required parameter: order_id",
 810 | 		},
 811 | 		{
 812 | 			Name: "invalid amount parameter type",
 813 | 			Request: map[string]interface{}{
 814 | 				"amount":   "not_a_number",
 815 | 				"token":    "token_MT48CvBhIC98MQ",
 816 | 				"order_id": "order_129837127313912",
 817 | 			},
 818 | 			MockHttpClient: nil,
 819 | 			ExpectError:    true,
 820 | 			ExpectedErrMsg: "invalid parameter type: amount",
 821 | 		},
 822 | 		{
 823 | 			Name: "multiple validation errors",
 824 | 			Request: map[string]interface{}{
 825 | 				"amount": "not_a_number",
 826 | 				"token":  123,
 827 | 				"email":  456,
 828 | 			},
 829 | 			MockHttpClient: nil,
 830 | 			ExpectError:    true,
 831 | 			ExpectedErrMsg: "Validation errors:\n- invalid parameter type: amount\n- " +
 832 | 				"invalid parameter type: token\n- " +
 833 | 				"missing required parameter: order_id\n- " +
 834 | 				"invalid parameter type: email",
 835 | 		},
 836 | 		{
 837 | 			Name: "successful UPI collect flow payment initiation",
 838 | 			Request: map[string]interface{}{
 839 | 				"amount":      10000,
 840 | 				"currency":    "INR",
 841 | 				"order_id":    "order_129837127313912",
 842 | 				"email":       "[email protected]",
 843 | 				"contact":     "9876543210",
 844 | 				"customer_id": "cust_RGCgP2osfPKFq2",
 845 | 				"save":        true,
 846 | 				"vpa":         "9876543210@ptsbi",
 847 | 			},
 848 | 			MockHttpClient: func() (*http.Client, *httptest.Server) {
 849 | 				successUpiCollectResp := map[string]interface{}{
 850 | 					"razorpay_payment_id": "pay_MT48CvBhIC98MQ",
 851 | 					"status":              "created",
 852 | 					"amount":              float64(10000),
 853 | 					"currency":            "INR",
 854 | 					"order_id":            "order_129837127313912",
 855 | 					"method":              "upi",
 856 | 					"next": []interface{}{
 857 | 						map[string]interface{}{
 858 | 							"action": "upi_collect",
 859 | 							"url": "https://api.razorpay.com/v1/payments/" +
 860 | 								"pay_MT48CvBhIC98MQ/authenticate",
 861 | 						},
 862 | 					},
 863 | 				}
 864 | 				return mock.NewHTTPClient(
 865 | 					mock.Endpoint{
 866 | 						Path:     createCustomerPath,
 867 | 						Method:   "POST",
 868 | 						Response: customerResp,
 869 | 					},
 870 | 					mock.Endpoint{
 871 | 						Path:     initiatePaymentPath,
 872 | 						Method:   "POST",
 873 | 						Response: successUpiCollectResp,
 874 | 					},
 875 | 				)
 876 | 			},
 877 | 			ExpectError: false,
 878 | 			ExpectedResult: map[string]interface{}{
 879 | 				"razorpay_payment_id": "pay_MT48CvBhIC98MQ",
 880 | 				"payment_details": map[string]interface{}{
 881 | 					"razorpay_payment_id": "pay_MT48CvBhIC98MQ",
 882 | 					"status":              "created",
 883 | 					"amount":              float64(10000),
 884 | 					"currency":            "INR",
 885 | 					"order_id":            "order_129837127313912",
 886 | 					"method":              "upi",
 887 | 					"next": []interface{}{
 888 | 						map[string]interface{}{
 889 | 							"action": "upi_collect",
 890 | 							"url": "https://api.razorpay.com/v1/payments/" +
 891 | 								"pay_MT48CvBhIC98MQ/authenticate",
 892 | 						},
 893 | 					},
 894 | 				},
 895 | 				"status":  "payment_initiated",
 896 | 				"message": "Payment initiated. Available actions: [upi_collect]",
 897 | 				"available_actions": []interface{}{
 898 | 					map[string]interface{}{
 899 | 						"action": "upi_collect",
 900 | 						"url": "https://api.razorpay.com/v1/payments/" +
 901 | 							"pay_MT48CvBhIC98MQ/authenticate",
 902 | 					},
 903 | 				},
 904 | 			},
 905 | 		},
 906 | 		{
 907 | 			Name: "successful UPI collect flow without token",
 908 | 			Request: map[string]interface{}{
 909 | 				"amount":   10000,
 910 | 				"order_id": "order_129837127313912",
 911 | 				"contact":  "9876543210",
 912 | 				"vpa":      "9876543210@ptsbi",
 913 | 			},
 914 | 			MockHttpClient: func() (*http.Client, *httptest.Server) {
 915 | 				successUpiCollectResp := map[string]interface{}{
 916 | 					"razorpay_payment_id": "pay_MT48CvBhIC98MQ",
 917 | 					"status":              "created",
 918 | 					"amount":              float64(10000),
 919 | 					"currency":            "INR",
 920 | 					"order_id":            "order_129837127313912",
 921 | 					"method":              "upi",
 922 | 				}
 923 | 				return mock.NewHTTPClient(
 924 | 					mock.Endpoint{
 925 | 						Path:     createCustomerPath,
 926 | 						Method:   "POST",
 927 | 						Response: customerResp,
 928 | 					},
 929 | 					mock.Endpoint{
 930 | 						Path:     initiatePaymentPath,
 931 | 						Method:   "POST",
 932 | 						Response: successUpiCollectResp,
 933 | 					},
 934 | 				)
 935 | 			},
 936 | 			ExpectError: false,
 937 | 			ExpectedResult: map[string]interface{}{
 938 | 				"razorpay_payment_id": "pay_MT48CvBhIC98MQ",
 939 | 				"payment_details": map[string]interface{}{
 940 | 					"razorpay_payment_id": "pay_MT48CvBhIC98MQ",
 941 | 					"status":              "created",
 942 | 					"amount":              float64(10000),
 943 | 					"currency":            "INR",
 944 | 					"order_id":            "order_129837127313912",
 945 | 					"method":              "upi",
 946 | 				},
 947 | 				"status": "payment_initiated",
 948 | 				"message": "Payment initiated successfully using " +
 949 | 					"S2S JSON v1 flow",
 950 | 				"next_step": "Use 'resend_otp' to regenerate OTP or " +
 951 | 					"'submit_otp' to proceed to enter OTP if " +
 952 | 					"OTP authentication is required.",
 953 | 				"next_tool": "resend_otp",
 954 | 				"next_tool_params": map[string]interface{}{
 955 | 					"payment_id": "pay_MT48CvBhIC98MQ",
 956 | 				},
 957 | 			},
 958 | 		},
 959 | 		{
 960 | 			Name: "UPI collect flow with all optional parameters",
 961 | 			Request: map[string]interface{}{
 962 | 				"amount":      10000,
 963 | 				"currency":    "INR",
 964 | 				"order_id":    "order_129837127313912",
 965 | 				"email":       "[email protected]",
 966 | 				"contact":     "9876543210",
 967 | 				"customer_id": "cust_RGCgP2osfPKFq2",
 968 | 				"save":        false,
 969 | 				"vpa":         "test@paytm",
 970 | 			},
 971 | 			MockHttpClient: func() (*http.Client, *httptest.Server) {
 972 | 				successUpiCollectResp := map[string]interface{}{
 973 | 					"razorpay_payment_id": "pay_MT48CvBhIC98MQ",
 974 | 					"status":              "created",
 975 | 					"amount":              float64(10000),
 976 | 					"currency":            "INR",
 977 | 					"order_id":            "order_129837127313912",
 978 | 					"method":              "upi",
 979 | 				}
 980 | 				return mock.NewHTTPClient(
 981 | 					mock.Endpoint{
 982 | 						Path:     createCustomerPath,
 983 | 						Method:   "POST",
 984 | 						Response: customerResp,
 985 | 					},
 986 | 					mock.Endpoint{
 987 | 						Path:     initiatePaymentPath,
 988 | 						Method:   "POST",
 989 | 						Response: successUpiCollectResp,
 990 | 					},
 991 | 				)
 992 | 			},
 993 | 			ExpectError: false,
 994 | 			ExpectedResult: map[string]interface{}{
 995 | 				"razorpay_payment_id": "pay_MT48CvBhIC98MQ",
 996 | 				"payment_details": map[string]interface{}{
 997 | 					"razorpay_payment_id": "pay_MT48CvBhIC98MQ",
 998 | 					"status":              "created",
 999 | 					"amount":              float64(10000),
1000 | 					"currency":            "INR",
1001 | 					"order_id":            "order_129837127313912",
1002 | 					"method":              "upi",
1003 | 				},
1004 | 				"status": "payment_initiated",
1005 | 				"message": "Payment initiated successfully using " +
1006 | 					"S2S JSON v1 flow",
1007 | 				"next_step": "Use 'resend_otp' to regenerate OTP or " +
1008 | 					"'submit_otp' to proceed to enter OTP if " +
1009 | 					"OTP authentication is required.",
1010 | 				"next_tool": "resend_otp",
1011 | 				"next_tool_params": map[string]interface{}{
1012 | 					"payment_id": "pay_MT48CvBhIC98MQ",
1013 | 				},
1014 | 			},
1015 | 		},
1016 | 		{
1017 | 			Name: "invalid save parameter type",
1018 | 			Request: map[string]interface{}{
1019 | 				"amount":   10000,
1020 | 				"order_id": "order_129837127313912",
1021 | 				"save":     "invalid_string_instead_of_bool",
1022 | 			},
1023 | 			MockHttpClient: nil,
1024 | 			ExpectError:    true,
1025 | 			ExpectedErrMsg: "invalid parameter type: save",
1026 | 		},
1027 | 		{
1028 | 			Name: "invalid customer_id parameter type",
1029 | 			Request: map[string]interface{}{
1030 | 				"amount":      10000,
1031 | 				"order_id":    "order_129837127313912",
1032 | 				"customer_id": 123,
1033 | 			},
1034 | 			MockHttpClient: nil,
1035 | 			ExpectError:    true,
1036 | 			ExpectedErrMsg: "invalid parameter type: customer_id",
1037 | 		},
1038 | 		{
1039 | 			Name: "successful UPI intent flow payment initiation",
1040 | 			Request: map[string]interface{}{
1041 | 				"amount":     12000,
1042 | 				"currency":   "INR",
1043 | 				"order_id":   "order_INTENT123",
1044 | 				"email":      "[email protected]",
1045 | 				"contact":    "9876543210",
1046 | 				"upi_intent": true,
1047 | 			},
1048 | 			MockHttpClient: func() (*http.Client, *httptest.Server) {
1049 | 				successUpiIntentResp := map[string]interface{}{
1050 | 					"razorpay_payment_id": "pay_INTENT123",
1051 | 					"status":              "created",
1052 | 					"amount":              float64(12000),
1053 | 					"currency":            "INR",
1054 | 					"order_id":            "order_INTENT123",
1055 | 					"method":              "upi",
1056 | 					"upi": map[string]interface{}{
1057 | 						"flow": "intent",
1058 | 					},
1059 | 					"next": []interface{}{
1060 | 						map[string]interface{}{
1061 | 							"action": "upi_intent",
1062 | 							"url": "https://api.razorpay.com/v1/payments/" +
1063 | 								"pay_INTENT123/upi_intent",
1064 | 						},
1065 | 					},
1066 | 				}
1067 | 				return mock.NewHTTPClient(
1068 | 					mock.Endpoint{
1069 | 						Path:     createCustomerPath,
1070 | 						Method:   "POST",
1071 | 						Response: customerResp,
1072 | 					},
1073 | 					mock.Endpoint{
1074 | 						Path:     initiatePaymentPath,
1075 | 						Method:   "POST",
1076 | 						Response: successUpiIntentResp,
1077 | 					},
1078 | 				)
1079 | 			},
1080 | 			ExpectError: false,
1081 | 			ExpectedResult: map[string]interface{}{
1082 | 				"razorpay_payment_id": "pay_INTENT123",
1083 | 				"payment_details": map[string]interface{}{
1084 | 					"razorpay_payment_id": "pay_INTENT123",
1085 | 					"status":              "created",
1086 | 					"amount":              float64(12000),
1087 | 					"currency":            "INR",
1088 | 					"order_id":            "order_INTENT123",
1089 | 					"method":              "upi",
1090 | 					"upi": map[string]interface{}{
1091 | 						"flow": "intent",
1092 | 					},
1093 | 					"next": []interface{}{
1094 | 						map[string]interface{}{
1095 | 							"action": "upi_intent",
1096 | 							"url": "https://api.razorpay.com/v1/payments/" +
1097 | 								"pay_INTENT123/upi_intent",
1098 | 						},
1099 | 					},
1100 | 				},
1101 | 				"status":  "payment_initiated",
1102 | 				"message": "Payment initiated. Available actions: [upi_intent]",
1103 | 				"available_actions": []interface{}{
1104 | 					map[string]interface{}{
1105 | 						"action": "upi_intent",
1106 | 						"url": "https://api.razorpay.com/v1/payments/" +
1107 | 							"pay_INTENT123/upi_intent",
1108 | 					},
1109 | 				},
1110 | 			},
1111 | 		},
1112 | 		{
1113 | 			Name: "invalid upi_intent parameter type",
1114 | 			Request: map[string]interface{}{
1115 | 				"amount":     10000,
1116 | 				"order_id":   "order_129837127313912",
1117 | 				"upi_intent": "invalid_string",
1118 | 			},
1119 | 			MockHttpClient: nil,
1120 | 			ExpectError:    true,
1121 | 			ExpectedErrMsg: "invalid parameter type: upi_intent",
1122 | 		},
1123 | 		{
1124 | 			Name: "successful payment initiation with force_terminal_id " +
1125 | 				"for single block multiple debit",
1126 | 			Request: map[string]interface{}{
1127 | 				"amount":            10000,
1128 | 				"currency":          "INR",
1129 | 				"order_id":          "order_129837127313912",
1130 | 				"email":             "[email protected]",
1131 | 				"contact":           "9876543210",
1132 | 				"customer_id":       "cust_RGCgP2osfPKFq2",
1133 | 				"recurring":         true,
1134 | 				"force_terminal_id": "term_ABCD1234256732",
1135 | 			},
1136 | 			MockHttpClient: func() (*http.Client, *httptest.Server) {
1137 | 				successPaymentWithTerminalResp := map[string]interface{}{
1138 | 					"razorpay_payment_id": "pay_MT48CvBhIC98MQ",
1139 | 					"status":              "created",
1140 | 					"amount":              float64(10000),
1141 | 					"currency":            "INR",
1142 | 					"order_id":            "order_129837127313912",
1143 | 					"method":              "upi",
1144 | 					"force_terminal_id":   "term_ABCD1234256732",
1145 | 				}
1146 | 				return mock.NewHTTPClient(
1147 | 					mock.Endpoint{
1148 | 						Path:     initiatePaymentPath,
1149 | 						Method:   "POST",
1150 | 						Response: successPaymentWithTerminalResp,
1151 | 					},
1152 | 				)
1153 | 			},
1154 | 			ExpectError: false,
1155 | 			ExpectedResult: map[string]interface{}{
1156 | 				"razorpay_payment_id": "pay_MT48CvBhIC98MQ",
1157 | 				"payment_details": map[string]interface{}{
1158 | 					"razorpay_payment_id": "pay_MT48CvBhIC98MQ",
1159 | 					"status":              "created",
1160 | 					"amount":              float64(10000),
1161 | 					"currency":            "INR",
1162 | 					"order_id":            "order_129837127313912",
1163 | 					"method":              "upi",
1164 | 					"force_terminal_id":   "term_ABCD1234256732",
1165 | 				},
1166 | 				"status":  "payment_initiated",
1167 | 				"message": "Payment initiated successfully using S2S JSON v1 flow",
1168 | 				"next_step": "Use 'resend_otp' to regenerate OTP or " +
1169 | 					"'submit_otp' to proceed to enter OTP if " +
1170 | 					"OTP authentication is required.",
1171 | 				"next_tool": "resend_otp",
1172 | 				"next_tool_params": map[string]interface{}{
1173 | 					"payment_id": "pay_MT48CvBhIC98MQ",
1174 | 				},
1175 | 			},
1176 | 		},
1177 | 		{
1178 | 			Name: "invalid force_terminal_id parameter type",
1179 | 			Request: map[string]interface{}{
1180 | 				"amount":            10000,
1181 | 				"order_id":          "order_129837127313912",
1182 | 				"force_terminal_id": 123,
1183 | 			},
1184 | 			MockHttpClient: nil,
1185 | 			ExpectError:    true,
1186 | 			ExpectedErrMsg: "invalid parameter type: force_terminal_id",
1187 | 		},
1188 | 	}
1189 | 
1190 | 	for _, tc := range tests {
1191 | 		t.Run(tc.Name, func(t *testing.T) {
1192 | 			runToolTest(t, tc, InitiatePayment, "Payment Initiation")
1193 | 		})
1194 | 	}
1195 | }
1196 | 
1197 | func Test_SubmitOtp(t *testing.T) {
1198 | 	submitOtpPathFmt := fmt.Sprintf(
1199 | 		"/%s%s/%%s/otp/submit",
1200 | 		constants.VERSION_V1,
1201 | 		constants.PAYMENT_URL,
1202 | 	)
1203 | 
1204 | 	successOtpSubmitResp := map[string]interface{}{
1205 | 		"id":                "pay_MT48CvBhIC98MQ",
1206 | 		"entity":            "payment",
1207 | 		"amount":            float64(10000),
1208 | 		"currency":          "INR",
1209 | 		"status":            "authorized",
1210 | 		"order_id":          "order_129837127313912",
1211 | 		"description":       "Test payment",
1212 | 		"method":            "card",
1213 | 		"amount_refunded":   float64(0),
1214 | 		"refund_status":     nil,
1215 | 		"captured":          false,
1216 | 		"email":             "[email protected]",
1217 | 		"contact":           "9876543210",
1218 | 		"fee":               float64(236),
1219 | 		"tax":               float64(36),
1220 | 		"error_code":        nil,
1221 | 		"error_description": nil,
1222 | 		"created_at":        float64(1234567890),
1223 | 	}
1224 | 
1225 | 	otpVerificationFailedResp := map[string]interface{}{
1226 | 		"error": map[string]interface{}{
1227 | 			"code":        "BAD_REQUEST_ERROR",
1228 | 			"description": "Invalid OTP provided",
1229 | 			"field":       "otp",
1230 | 		},
1231 | 	}
1232 | 
1233 | 	paymentNotFoundResp := map[string]interface{}{
1234 | 		"error": map[string]interface{}{
1235 | 			"code":        "BAD_REQUEST_ERROR",
1236 | 			"description": "Payment not found",
1237 | 		},
1238 | 	}
1239 | 
1240 | 	tests := []RazorpayToolTestCase{
1241 | 		{
1242 | 			Name: "successful OTP submission",
1243 | 			Request: map[string]interface{}{
1244 | 				"payment_id": "pay_MT48CvBhIC98MQ",
1245 | 				"otp_string": "123456",
1246 | 			},
1247 | 			MockHttpClient: func() (*http.Client, *httptest.Server) {
1248 | 				return mock.NewHTTPClient(
1249 | 					mock.Endpoint{
1250 | 						Path:     fmt.Sprintf(submitOtpPathFmt, "pay_MT48CvBhIC98MQ"),
1251 | 						Method:   "POST",
1252 | 						Response: successOtpSubmitResp,
1253 | 					},
1254 | 				)
1255 | 			},
1256 | 			ExpectError: false,
1257 | 			ExpectedResult: map[string]interface{}{
1258 | 				"payment_id":    "pay_MT48CvBhIC98MQ",
1259 | 				"status":        "success",
1260 | 				"message":       "OTP verified successfully.",
1261 | 				"response_data": successOtpSubmitResp,
1262 | 			},
1263 | 		},
1264 | 		{
1265 | 			Name: "OTP verification failed - invalid OTP",
1266 | 			Request: map[string]interface{}{
1267 | 				"payment_id": "pay_MT48CvBhIC98MQ",
1268 | 				"otp_string": "000000",
1269 | 			},
1270 | 			MockHttpClient: func() (*http.Client, *httptest.Server) {
1271 | 				return mock.NewHTTPClient(
1272 | 					mock.Endpoint{
1273 | 						Path:     fmt.Sprintf(submitOtpPathFmt, "pay_MT48CvBhIC98MQ"),
1274 | 						Method:   "POST",
1275 | 						Response: otpVerificationFailedResp,
1276 | 					},
1277 | 				)
1278 | 			},
1279 | 			ExpectError:    true,
1280 | 			ExpectedErrMsg: "OTP verification failed: Invalid OTP provided",
1281 | 		},
1282 | 		{
1283 | 			Name: "payment not found",
1284 | 			Request: map[string]interface{}{
1285 | 				"payment_id": "pay_invalid",
1286 | 				"otp_string": "123456",
1287 | 			},
1288 | 			MockHttpClient: func() (*http.Client, *httptest.Server) {
1289 | 				return mock.NewHTTPClient(
1290 | 					mock.Endpoint{
1291 | 						Path:     fmt.Sprintf(submitOtpPathFmt, "pay_invalid"),
1292 | 						Method:   "POST",
1293 | 						Response: paymentNotFoundResp,
1294 | 					},
1295 | 				)
1296 | 			},
1297 | 			ExpectError:    true,
1298 | 			ExpectedErrMsg: "OTP verification failed: Payment not found",
1299 | 		},
1300 | 		{
1301 | 			Name: "missing payment_id parameter",
1302 | 			Request: map[string]interface{}{
1303 | 				"otp_string": "123456",
1304 | 			},
1305 | 			MockHttpClient: nil, // No HTTP client needed for validation error
1306 | 			ExpectError:    true,
1307 | 			ExpectedErrMsg: "missing required parameter: payment_id",
1308 | 		},
1309 | 		{
1310 | 			Name: "missing otp_string parameter",
1311 | 			Request: map[string]interface{}{
1312 | 				"payment_id": "pay_MT48CvBhIC98MQ",
1313 | 			},
1314 | 			MockHttpClient: nil, // No HTTP client needed for validation error
1315 | 			ExpectError:    true,
1316 | 			ExpectedErrMsg: "missing required parameter: otp_string",
1317 | 		},
1318 | 		{
1319 | 			Name:           "missing both required parameters",
1320 | 			Request:        map[string]interface{}{},
1321 | 			MockHttpClient: nil, // No HTTP client needed for validation error
1322 | 			ExpectError:    true,
1323 | 			ExpectedErrMsg: "missing required parameter: otp_string",
1324 | 		},
1325 | 		{
1326 | 			Name: "empty otp_string",
1327 | 			Request: map[string]interface{}{
1328 | 				"payment_id": "pay_MT48CvBhIC98MQ",
1329 | 				"otp_string": "",
1330 | 			},
1331 | 			MockHttpClient: func() (*http.Client, *httptest.Server) {
1332 | 				return mock.NewHTTPClient(
1333 | 					mock.Endpoint{
1334 | 						Path:   fmt.Sprintf(submitOtpPathFmt, "pay_MT48CvBhIC98MQ"),
1335 | 						Method: "POST",
1336 | 						Response: map[string]interface{}{
1337 | 							"error": map[string]interface{}{
1338 | 								"code":        "BAD_REQUEST_ERROR",
1339 | 								"description": "Authentication failed",
1340 | 							},
1341 | 						},
1342 | 					},
1343 | 				)
1344 | 			},
1345 | 			ExpectError:    true,
1346 | 			ExpectedErrMsg: "OTP verification failed: Authentication failed",
1347 | 		},
1348 | 		{
1349 | 			Name: "empty payment_id",
1350 | 			Request: map[string]interface{}{
1351 | 				"payment_id": "",
1352 | 				"otp_string": "123456",
1353 | 			},
1354 | 			MockHttpClient: func() (*http.Client, *httptest.Server) {
1355 | 				return mock.NewHTTPClient(
1356 | 					mock.Endpoint{
1357 | 						Path:   fmt.Sprintf(submitOtpPathFmt, ""),
1358 | 						Method: "POST",
1359 | 						Response: map[string]interface{}{
1360 | 							"error": map[string]interface{}{
1361 | 								"code":        "BAD_REQUEST_ERROR",
1362 | 								"description": "",
1363 | 							},
1364 | 						},
1365 | 					},
1366 | 				)
1367 | 			},
1368 | 			ExpectError:    true,
1369 | 			ExpectedErrMsg: "OTP verification failed:",
1370 | 		},
1371 | 	}
1372 | 
1373 | 	for _, tc := range tests {
1374 | 		t.Run(tc.Name, func(t *testing.T) {
1375 | 			runToolTest(t, tc, SubmitOtp, "OTP Submission")
1376 | 		})
1377 | 	}
1378 | }
1379 | 
1380 | func Test_InitiatePaymentWithVPA(t *testing.T) {
1381 | 	initiatePaymentPath := fmt.Sprintf(
1382 | 		"/%s%s/create/json",
1383 | 		constants.VERSION_V1,
1384 | 		constants.PAYMENT_URL,
1385 | 	)
1386 | 
1387 | 	createCustomerPath := fmt.Sprintf(
1388 | 		"/%s%s",
1389 | 		constants.VERSION_V1,
1390 | 		constants.CUSTOMER_URL,
1391 | 	)
1392 | 
1393 | 	customerResp := map[string]interface{}{
1394 | 		"id":         "cust_1Aa00000000003",
1395 | 		"entity":     "customer",
1396 | 		"name":       "",
1397 | 		"email":      "",
1398 | 		"contact":    "9876543210",
1399 | 		"gstin":      nil,
1400 | 		"notes":      map[string]interface{}{},
1401 | 		"created_at": float64(1234567890),
1402 | 	}
1403 | 
1404 | 	testCases := []RazorpayToolTestCase{
1405 | 		{
1406 | 			Name: "successful UPI payment with VPA parameter",
1407 | 			Request: map[string]interface{}{
1408 | 				"amount":   10000,
1409 | 				"order_id": "order_129837127313912",
1410 | 				"vpa":      "9876543210@ptsbi",
1411 | 				"email":    "[email protected]",
1412 | 				"contact":  "9876543210",
1413 | 			},
1414 | 			MockHttpClient: func() (*http.Client, *httptest.Server) {
1415 | 				successUpiVpaResp := map[string]interface{}{
1416 | 					"razorpay_payment_id": "pay_MT48CvBhIC98MQ",
1417 | 					"status":              "created",
1418 | 					"amount":              float64(10000),
1419 | 					"currency":            "INR",
1420 | 					"order_id":            "order_129837127313912",
1421 | 					"method":              "upi",
1422 | 					"email":               "[email protected]",
1423 | 					"contact":             "9876543210",
1424 | 					"upi_transaction_id":  nil,
1425 | 					"upi": map[string]interface{}{
1426 | 						"flow":        "collect",
1427 | 						"expiry_time": "6",
1428 | 						"vpa":         "9876543210@ptsbi",
1429 | 					},
1430 | 					"next": []interface{}{
1431 | 						map[string]interface{}{
1432 | 							"action": "upi_collect",
1433 | 							"url": "https://api.razorpay.com/v1/payments/" +
1434 | 								"pay_MT48CvBhIC98MQ/otp_generate",
1435 | 						},
1436 | 					},
1437 | 				}
1438 | 				return mock.NewHTTPClient(
1439 | 					mock.Endpoint{
1440 | 						Path:     createCustomerPath,
1441 | 						Method:   "POST",
1442 | 						Response: customerResp,
1443 | 					},
1444 | 					mock.Endpoint{
1445 | 						Path:     initiatePaymentPath,
1446 | 						Method:   "POST",
1447 | 						Response: successUpiVpaResp,
1448 | 					},
1449 | 				)
1450 | 			},
1451 | 			ExpectError: false,
1452 | 			ExpectedResult: map[string]interface{}{
1453 | 				"razorpay_payment_id": "pay_MT48CvBhIC98MQ",
1454 | 				"payment_details": map[string]interface{}{
1455 | 					"razorpay_payment_id": "pay_MT48CvBhIC98MQ",
1456 | 					"status":              "created",
1457 | 					"amount":              float64(10000),
1458 | 					"currency":            "INR",
1459 | 					"order_id":            "order_129837127313912",
1460 | 					"method":              "upi",
1461 | 					"email":               "[email protected]",
1462 | 					"contact":             "9876543210",
1463 | 					"upi_transaction_id":  nil,
1464 | 					"upi": map[string]interface{}{
1465 | 						"flow":        "collect",
1466 | 						"expiry_time": "6",
1467 | 						"vpa":         "9876543210@ptsbi",
1468 | 					},
1469 | 					"next": []interface{}{
1470 | 						map[string]interface{}{
1471 | 							"action": "upi_collect",
1472 | 							"url": "https://api.razorpay.com/v1/payments/" +
1473 | 								"pay_MT48CvBhIC98MQ/otp_generate",
1474 | 						},
1475 | 					},
1476 | 				},
1477 | 				"status":  "payment_initiated",
1478 | 				"message": "Payment initiated. Available actions: [upi_collect]",
1479 | 				"available_actions": []interface{}{
1480 | 					map[string]interface{}{
1481 | 						"action": "upi_collect",
1482 | 						"url": "https://api.razorpay.com/v1/payments/" +
1483 | 							"pay_MT48CvBhIC98MQ/otp_generate",
1484 | 					},
1485 | 				},
1486 | 			},
1487 | 		},
1488 | 		{
1489 | 			Name: "UPI payment with VPA and custom currency",
1490 | 			Request: map[string]interface{}{
1491 | 				"amount":   20000,
1492 | 				"currency": "INR",
1493 | 				"order_id": "order_ABC123XYZ456",
1494 | 				"vpa":      "test@upi",
1495 | 			},
1496 | 			MockHttpClient: func() (*http.Client, *httptest.Server) {
1497 | 				successUpiVpaResp := map[string]interface{}{
1498 | 					"razorpay_payment_id": "pay_ABC123XYZ456",
1499 | 					"status":              "created",
1500 | 					"amount":              float64(20000),
1501 | 					"currency":            "INR",
1502 | 					"order_id":            "order_ABC123XYZ456",
1503 | 					"method":              "upi",
1504 | 					"upi": map[string]interface{}{
1505 | 						"flow":        "collect",
1506 | 						"expiry_time": "6",
1507 | 						"vpa":         "test@upi",
1508 | 					},
1509 | 				}
1510 | 				return mock.NewHTTPClient(
1511 | 					mock.Endpoint{
1512 | 						Path:     initiatePaymentPath,
1513 | 						Method:   "POST",
1514 | 						Response: successUpiVpaResp,
1515 | 					},
1516 | 				)
1517 | 			},
1518 | 			ExpectError: false,
1519 | 			ExpectedResult: map[string]interface{}{
1520 | 				"razorpay_payment_id": "pay_ABC123XYZ456",
1521 | 				"payment_details": map[string]interface{}{
1522 | 					"razorpay_payment_id": "pay_ABC123XYZ456",
1523 | 					"status":              "created",
1524 | 					"amount":              float64(20000),
1525 | 					"currency":            "INR",
1526 | 					"order_id":            "order_ABC123XYZ456",
1527 | 					"method":              "upi",
1528 | 					"upi": map[string]interface{}{
1529 | 						"flow":        "collect",
1530 | 						"expiry_time": "6",
1531 | 						"vpa":         "test@upi",
1532 | 					},
1533 | 				},
1534 | 				"status": "payment_initiated",
1535 | 				"message": "Payment initiated successfully using " +
1536 | 					"S2S JSON v1 flow",
1537 | 				"next_step": "Use 'resend_otp' to regenerate OTP or " +
1538 | 					"'submit_otp' to proceed to enter OTP if " +
1539 | 					"OTP authentication is required.",
1540 | 				"next_tool": "resend_otp",
1541 | 				"next_tool_params": map[string]interface{}{
1542 | 					"payment_id": "pay_ABC123XYZ456",
1543 | 				},
1544 | 			},
1545 | 		},
1546 | 		{
1547 | 			Name: "missing VPA parameter value",
1548 | 			Request: map[string]interface{}{
1549 | 				"amount":   10000,
1550 | 				"order_id": "order_129837127313912",
1551 | 				"vpa":      "",
1552 | 			},
1553 | 			MockHttpClient: func() (*http.Client, *httptest.Server) {
1554 | 				successRegularResp := map[string]interface{}{
1555 | 					"razorpay_payment_id": "pay_MT48CvBhIC98MQ",
1556 | 					"status":              "created",
1557 | 					"amount":              float64(10000),
1558 | 					"currency":            "INR",
1559 | 					"order_id":            "order_129837127313912",
1560 | 				}
1561 | 				return mock.NewHTTPClient(
1562 | 					mock.Endpoint{
1563 | 						Path:     initiatePaymentPath,
1564 | 						Method:   "POST",
1565 | 						Response: successRegularResp,
1566 | 					},
1567 | 				)
1568 | 			},
1569 | 			ExpectError: false, // Empty VPA should not trigger UPI logic
1570 | 			ExpectedResult: map[string]interface{}{
1571 | 				"razorpay_payment_id": "pay_MT48CvBhIC98MQ",
1572 | 				"payment_details": map[string]interface{}{
1573 | 					"razorpay_payment_id": "pay_MT48CvBhIC98MQ",
1574 | 					"status":              "created",
1575 | 					"amount":              float64(10000),
1576 | 					"currency":            "INR",
1577 | 					"order_id":            "order_129837127313912",
1578 | 				},
1579 | 				"status": "payment_initiated",
1580 | 				"message": "Payment initiated successfully using " +
1581 | 					"S2S JSON v1 flow",
1582 | 				"next_step": "Use 'resend_otp' to regenerate OTP or " +
1583 | 					"'submit_otp' to proceed to enter OTP if " +
1584 | 					"OTP authentication is required.",
1585 | 				"next_tool": "resend_otp",
1586 | 				"next_tool_params": map[string]interface{}{
1587 | 					"payment_id": "pay_MT48CvBhIC98MQ",
1588 | 				},
1589 | 			},
1590 | 		},
1591 | 		{
1592 | 			Name: "VPA parameter automatically sets UPI method",
1593 | 			Request: map[string]interface{}{
1594 | 				"amount":   15000,
1595 | 				"order_id": "order_OVERRIDE123",
1596 | 				"vpa":      "new@upi",
1597 | 			},
1598 | 			MockHttpClient: func() (*http.Client, *httptest.Server) {
1599 | 				successUpiOverrideResp := map[string]interface{}{
1600 | 					"razorpay_payment_id": "pay_OVERRIDE123",
1601 | 					"status":              "created",
1602 | 					"amount":              float64(15000),
1603 | 					"currency":            "INR",
1604 | 					"order_id":            "order_OVERRIDE123",
1605 | 					"method":              "upi", // Should be set to UPI by VPA
1606 | 					"upi": map[string]interface{}{
1607 | 						"flow":        "collect", // Default flow
1608 | 						"expiry_time": "6",       // Default expiry
1609 | 						"vpa":         "new@upi", // VPA from parameter
1610 | 					},
1611 | 					"next": []interface{}{
1612 | 						map[string]interface{}{
1613 | 							"action": "upi_collect",
1614 | 							"url": "https://api.razorpay.com/v1/payments/" +
1615 | 								"pay_OVERRIDE123/otp_generate",
1616 | 						},
1617 | 					},
1618 | 				}
1619 | 				return mock.NewHTTPClient(
1620 | 					mock.Endpoint{
1621 | 						Path:     initiatePaymentPath,
1622 | 						Method:   "POST",
1623 | 						Response: successUpiOverrideResp,
1624 | 					},
1625 | 				)
1626 | 			},
1627 | 			ExpectError: false,
1628 | 			ExpectedResult: map[string]interface{}{
1629 | 				"razorpay_payment_id": "pay_OVERRIDE123",
1630 | 				"payment_details": map[string]interface{}{
1631 | 					"razorpay_payment_id": "pay_OVERRIDE123",
1632 | 					"status":              "created",
1633 | 					"amount":              float64(15000),
1634 | 					"currency":            "INR",
1635 | 					"order_id":            "order_OVERRIDE123",
1636 | 					"method":              "upi",
1637 | 					"upi": map[string]interface{}{
1638 | 						"flow":        "collect",
1639 | 						"expiry_time": "6",
1640 | 						"vpa":         "new@upi",
1641 | 					},
1642 | 					"next": []interface{}{
1643 | 						map[string]interface{}{
1644 | 							"action": "upi_collect",
1645 | 							"url": "https://api.razorpay.com/v1/payments/" +
1646 | 								"pay_OVERRIDE123/otp_generate",
1647 | 						},
1648 | 					},
1649 | 				},
1650 | 				"status":  "payment_initiated",
1651 | 				"message": "Payment initiated. Available actions: [upi_collect]",
1652 | 				"available_actions": []interface{}{
1653 | 					map[string]interface{}{
1654 | 						"action": "upi_collect",
1655 | 						"url": "https://api.razorpay.com/v1/payments/" +
1656 | 							"pay_OVERRIDE123/otp_generate",
1657 | 					},
1658 | 				},
1659 | 			},
1660 | 		},
1661 | 	}
1662 | 
1663 | 	for _, tc := range testCases {
1664 | 		t.Run(tc.Name, func(t *testing.T) {
1665 | 			runToolTest(t, tc, InitiatePayment, "Payment")
1666 | 		})
1667 | 	}
1668 | }
1669 | 
1670 | // Test helper functions for better coverage
1671 | func Test_extractPaymentID(t *testing.T) {
1672 | 	tests := []struct {
1673 | 		name     string
1674 | 		payment  map[string]interface{}
1675 | 		expected string
1676 | 	}{
1677 | 		{
1678 | 			name: "valid payment ID",
1679 | 			payment: map[string]interface{}{
1680 | 				"razorpay_payment_id": "pay_MT48CvBhIC98MQ",
1681 | 				"status":              "created",
1682 | 			},
1683 | 			expected: "pay_MT48CvBhIC98MQ",
1684 | 		},
1685 | 		{
1686 | 			name: "missing payment ID",
1687 | 			payment: map[string]interface{}{
1688 | 				"status": "created",
1689 | 			},
1690 | 			expected: "",
1691 | 		},
1692 | 		{
1693 | 			name: "nil payment ID",
1694 | 			payment: map[string]interface{}{
1695 | 				"razorpay_payment_id": nil,
1696 | 				"status":              "created",
1697 | 			},
1698 | 			expected: "",
1699 | 		},
1700 | 		{
1701 | 			name:     "empty payment map",
1702 | 			payment:  map[string]interface{}{},
1703 | 			expected: "",
1704 | 		},
1705 | 	}
1706 | 
1707 | 	for _, tt := range tests {
1708 | 		t.Run(tt.name, func(t *testing.T) {
1709 | 			result := extractPaymentID(tt.payment)
1710 | 			if result != tt.expected {
1711 | 				t.Errorf("extractPaymentID() = %v, want %v", result, tt.expected)
1712 | 			}
1713 | 		})
1714 | 	}
1715 | }
1716 | 
1717 | func Test_buildInitiatePaymentResponse(t *testing.T) {
1718 | 	tests := []struct {
1719 | 		name           string
1720 | 		payment        map[string]interface{}
1721 | 		paymentID      string
1722 | 		actions        []map[string]interface{}
1723 | 		expectedMsg    string
1724 | 		expectedOtpURL string
1725 | 	}{
1726 | 		{
1727 | 			name: "payment with OTP action",
1728 | 			payment: map[string]interface{}{
1729 | 				"id":     "pay_MT48CvBhIC98MQ",
1730 | 				"status": "created",
1731 | 			},
1732 | 			paymentID: "pay_MT48CvBhIC98MQ",
1733 | 			actions: []map[string]interface{}{
1734 | 				{
1735 | 					"action": "otp_generate",
1736 | 					"url": "https://api.razorpay.com/v1/payments/" +
1737 | 						"pay_MT48CvBhIC98MQ/otp_generate",
1738 | 				},
1739 | 			},
1740 | 			expectedMsg: "Payment initiated. OTP authentication is available. " +
1741 | 				"Use the 'submit_otp' tool to submit OTP received by the customer " +
1742 | 				"for authentication.",
1743 | 			expectedOtpURL: "https://api.razorpay.com/v1/payments/" +
1744 | 				"pay_MT48CvBhIC98MQ/otp_generate",
1745 | 		},
1746 | 		{
1747 | 			name: "payment with redirect action",
1748 | 			payment: map[string]interface{}{
1749 | 				"id":     "pay_MT48CvBhIC98MQ",
1750 | 				"status": "created",
1751 | 			},
1752 | 			paymentID: "pay_MT48CvBhIC98MQ",
1753 | 			actions: []map[string]interface{}{
1754 | 				{
1755 | 					"action": "redirect",
1756 | 					"url": "https://api.razorpay.com/v1/payments/" +
1757 | 						"pay_MT48CvBhIC98MQ/authenticate",
1758 | 				},
1759 | 			},
1760 | 			expectedMsg: "Payment initiated. Redirect authentication is available. " +
1761 | 				"Use the redirect URL provided in available_actions.",
1762 | 			expectedOtpURL: "",
1763 | 		},
1764 | 		{
1765 | 			name: "payment with UPI collect action",
1766 | 			payment: map[string]interface{}{
1767 | 				"id":     "pay_MT48CvBhIC98MQ",
1768 | 				"status": "created",
1769 | 			},
1770 | 			paymentID: "pay_MT48CvBhIC98MQ",
1771 | 			actions: []map[string]interface{}{
1772 | 				{
1773 | 					"action": "upi_collect",
1774 | 					"url": "https://api.razorpay.com/v1/payments/" +
1775 | 						"pay_MT48CvBhIC98MQ/authenticate",
1776 | 				},
1777 | 			},
1778 | 			expectedMsg:    "Payment initiated. Available actions: [upi_collect]",
1779 | 			expectedOtpURL: "",
1780 | 		},
1781 | 		{
1782 | 			name: "payment with multiple actions including OTP",
1783 | 			payment: map[string]interface{}{
1784 | 				"id":     "pay_MT48CvBhIC98MQ",
1785 | 				"status": "created",
1786 | 			},
1787 | 			paymentID: "pay_MT48CvBhIC98MQ",
1788 | 			actions: []map[string]interface{}{
1789 | 				{
1790 | 					"action": "otp_generate",
1791 | 					"url": "https://api.razorpay.com/v1/payments/" +
1792 | 						"pay_MT48CvBhIC98MQ/otp_generate",
1793 | 				},
1794 | 				{
1795 | 					"action": "redirect",
1796 | 					"url": "https://api.razorpay.com/v1/payments/" +
1797 | 						"pay_MT48CvBhIC98MQ/authenticate",
1798 | 				},
1799 | 			},
1800 | 			expectedMsg: "Payment initiated. OTP authentication is available. " +
1801 | 				"Use the 'submit_otp' tool to submit OTP received by the customer " +
1802 | 				"for authentication.",
1803 | 			expectedOtpURL: "https://api.razorpay.com/v1/payments/" +
1804 | 				"pay_MT48CvBhIC98MQ/otp_generate",
1805 | 		},
1806 | 		{
1807 | 			name: "payment with no actions",
1808 | 			payment: map[string]interface{}{
1809 | 				"id":     "pay_MT48CvBhIC98MQ",
1810 | 				"status": "captured",
1811 | 			},
1812 | 			paymentID:      "pay_MT48CvBhIC98MQ",
1813 | 			actions:        []map[string]interface{}{},
1814 | 			expectedMsg:    "Payment initiated successfully using S2S JSON v1 flow",
1815 | 			expectedOtpURL: "",
1816 | 		},
1817 | 		{
1818 | 			name: "payment with unknown action",
1819 | 			payment: map[string]interface{}{
1820 | 				"id":     "pay_MT48CvBhIC98MQ",
1821 | 				"status": "created",
1822 | 			},
1823 | 			paymentID: "pay_MT48CvBhIC98MQ",
1824 | 			actions: []map[string]interface{}{
1825 | 				{
1826 | 					"action": "unknown_action",
1827 | 					"url": "https://api.razorpay.com/v1/payments/" +
1828 | 						"pay_MT48CvBhIC98MQ/unknown",
1829 | 				},
1830 | 			},
1831 | 			expectedMsg:    "Payment initiated. Available actions: [unknown_action]",
1832 | 			expectedOtpURL: "",
1833 | 		},
1834 | 	}
1835 | 
1836 | 	for _, tt := range tests {
1837 | 		t.Run(tt.name, func(t *testing.T) {
1838 | 			response, otpURL := buildInitiatePaymentResponse(
1839 | 				tt.payment, tt.paymentID, tt.actions)
1840 | 
1841 | 			// Check basic response structure
1842 | 			if response["razorpay_payment_id"] != tt.paymentID {
1843 | 				t.Errorf("Expected payment ID %s, got %v", tt.paymentID,
1844 | 					response["razorpay_payment_id"])
1845 | 			}
1846 | 
1847 | 			if response["status"] != "payment_initiated" {
1848 | 				t.Errorf("Expected status 'payment_initiated', got %v", response["status"])
1849 | 			}
1850 | 
1851 | 			// Check message
1852 | 			if response["message"] != tt.expectedMsg {
1853 | 				t.Errorf("Expected message %s, got %v", tt.expectedMsg, response["message"])
1854 | 			}
1855 | 
1856 | 			// Check OTP URL
1857 | 			if otpURL != tt.expectedOtpURL {
1858 | 				t.Errorf("Expected OTP URL %s, got %s", tt.expectedOtpURL, otpURL)
1859 | 			}
1860 | 
1861 | 			// Check actions are included when present
1862 | 			if len(tt.actions) > 0 {
1863 | 				if _, exists := response["available_actions"]; !exists {
1864 | 					t.Error("Expected available_actions to be present in response")
1865 | 				}
1866 | 			}
1867 | 
1868 | 			// Check next step instructions for OTP case
1869 | 			if tt.paymentID != "" && len(tt.actions) == 0 {
1870 | 				if _, exists := response["next_step"]; !exists {
1871 | 					t.Error("Expected next_step to be present for fallback case")
1872 | 				}
1873 | 			}
1874 | 		})
1875 | 	}
1876 | }
1877 | 
1878 | func Test_addNextStepInstructions(t *testing.T) {
1879 | 	tests := []struct {
1880 | 		name      string
1881 | 		paymentID string
1882 | 		expected  bool // whether next_step should be added
1883 | 	}{
1884 | 		{
1885 | 			name:      "valid payment ID",
1886 | 			paymentID: "pay_MT48CvBhIC98MQ",
1887 | 			expected:  true,
1888 | 		},
1889 | 		{
1890 | 			name:      "empty payment ID",
1891 | 			paymentID: "",
1892 | 			expected:  false,
1893 | 		},
1894 | 	}
1895 | 
1896 | 	for _, tt := range tests {
1897 | 		t.Run(tt.name, func(t *testing.T) {
1898 | 			response := make(map[string]interface{})
1899 | 			addNextStepInstructions(response, tt.paymentID)
1900 | 
1901 | 			if tt.expected {
1902 | 				if _, exists := response["next_step"]; !exists {
1903 | 					t.Error("Expected next_step to be added")
1904 | 				}
1905 | 				if _, exists := response["next_tool"]; !exists {
1906 | 					t.Error("Expected next_tool to be added")
1907 | 				}
1908 | 				if _, exists := response["next_tool_params"]; !exists {
1909 | 					t.Error("Expected next_tool_params to be added")
1910 | 				}
1911 | 
1912 | 				// Check specific values
1913 | 				if response["next_tool"] != "resend_otp" {
1914 | 					t.Errorf("Expected next_tool to be 'resend_otp', got %v",
1915 | 						response["next_tool"])
1916 | 				}
1917 | 
1918 | 				params, ok := response["next_tool_params"].(map[string]interface{})
1919 | 				if !ok || params["payment_id"] != tt.paymentID {
1920 | 					t.Errorf("Expected next_tool_params to contain payment_id %s",
1921 | 						tt.paymentID)
1922 | 				}
1923 | 			} else {
1924 | 				if _, exists := response["next_step"]; exists {
1925 | 					t.Error("Expected next_step NOT to be added for empty payment ID")
1926 | 				}
1927 | 			}
1928 | 		})
1929 | 	}
1930 | }
1931 | 
1932 | func Test_sendOtp_validation(t *testing.T) {
1933 | 	tests := []struct {
1934 | 		name        string
1935 | 		otpURL      string
1936 | 		expectedErr string
1937 | 	}{
1938 | 		{
1939 | 			name:        "empty URL",
1940 | 			otpURL:      "",
1941 | 			expectedErr: "OTP URL is empty",
1942 | 		},
1943 | 		{
1944 | 			name:        "invalid URL",
1945 | 			otpURL:      "not-a-valid-url",
1946 | 			expectedErr: "OTP URL must use HTTPS",
1947 | 		},
1948 | 		{
1949 | 			name:        "non-HTTPS URL",
1950 | 			otpURL:      "http://api.razorpay.com/v1/payments/pay_123/otp_generate",
1951 | 			expectedErr: "OTP URL must use HTTPS",
1952 | 		},
1953 | 		{
1954 | 			name:        "non-Razorpay domain",
1955 | 			otpURL:      "https://malicious.com/v1/payments/pay_123/otp_generate",
1956 | 			expectedErr: "OTP URL must be from Razorpay domain",
1957 | 		},
1958 | 		{
1959 | 			name:        "valid Razorpay URL - should fail at HTTP call",
1960 | 			otpURL:      "https://api.razorpay.com/v1/payments/pay_123/otp_generate",
1961 | 			expectedErr: "OTP generation failed",
1962 | 		},
1963 | 	}
1964 | 
1965 | 	for _, tt := range tests {
1966 | 		t.Run(tt.name, func(t *testing.T) {
1967 | 			err := sendOtp(tt.otpURL)
1968 | 			if err == nil {
1969 | 				t.Error("Expected error but got nil")
1970 | 				return
1971 | 			}
1972 | 
1973 | 			if tt.expectedErr != "" && !strings.Contains(err.Error(), tt.expectedErr) {
1974 | 				t.Errorf("Expected error to contain '%s', got '%s'",
1975 | 					tt.expectedErr, err.Error())
1976 | 			}
1977 | 		})
1978 | 	}
1979 | }
1980 | 
1981 | func Test_extractOtpSubmitURL(t *testing.T) {
1982 | 	tests := []struct {
1983 | 		name     string
1984 | 		payment  map[string]interface{}
1985 | 		expected string
1986 | 	}{
1987 | 		{
1988 | 			name: "payment with next actions containing otp_submit",
1989 | 			payment: map[string]interface{}{
1990 | 				"next": []interface{}{
1991 | 					map[string]interface{}{
1992 | 						"action": "otp_submit",
1993 | 						"url":    "https://api.razorpay.com/v1/payments/pay_123/otp/submit",
1994 | 					},
1995 | 				},
1996 | 			},
1997 | 			expected: "https://api.razorpay.com/v1/payments/pay_123/otp/submit",
1998 | 		},
1999 | 		{
2000 | 			name: "payment with multiple next actions",
2001 | 			payment: map[string]interface{}{
2002 | 				"next": []interface{}{
2003 | 					map[string]interface{}{
2004 | 						"action": "redirect",
2005 | 						"url":    "https://api.razorpay.com/v1/payments/pay_123/authenticate",
2006 | 					},
2007 | 					map[string]interface{}{
2008 | 						"action": "otp_submit",
2009 | 						"url":    "https://api.razorpay.com/v1/payments/pay_123/otp/submit",
2010 | 					},
2011 | 				},
2012 | 			},
2013 | 			expected: "https://api.razorpay.com/v1/payments/pay_123/otp/submit",
2014 | 		},
2015 | 		{
2016 | 			name: "payment with no next actions",
2017 | 			payment: map[string]interface{}{
2018 | 				"status": "captured",
2019 | 			},
2020 | 			expected: "",
2021 | 		},
2022 | 		{
2023 | 			name: "payment with next actions but no otp_submit",
2024 | 			payment: map[string]interface{}{
2025 | 				"next": []interface{}{
2026 | 					map[string]interface{}{
2027 | 						"action": "redirect",
2028 | 						"url":    "https://api.razorpay.com/v1/payments/pay_123/authenticate",
2029 | 					},
2030 | 				},
2031 | 			},
2032 | 			expected: "",
2033 | 		},
2034 | 		{
2035 | 			name: "payment with empty next array",
2036 | 			payment: map[string]interface{}{
2037 | 				"next": []interface{}{},
2038 | 			},
2039 | 			expected: "",
2040 | 		},
2041 | 		{
2042 | 			name: "payment with invalid next structure",
2043 | 			payment: map[string]interface{}{
2044 | 				"next": "invalid_structure",
2045 | 			},
2046 | 			expected: "",
2047 | 		},
2048 | 		{
2049 | 			name: "payment with otp_submit action but nil URL",
2050 | 			payment: map[string]interface{}{
2051 | 				"next": []interface{}{
2052 | 					map[string]interface{}{
2053 | 						"action": "otp_submit",
2054 | 						"url":    nil, // nil URL should return empty string
2055 | 					},
2056 | 				},
2057 | 			},
2058 | 			expected: "",
2059 | 		},
2060 | 		{
2061 | 			name: "payment with otp_submit action but non-string URL",
2062 | 			payment: map[string]interface{}{
2063 | 				"next": []interface{}{
2064 | 					map[string]interface{}{
2065 | 						"action": "otp_submit",
2066 | 						"url":    123, // non-string URL should cause type assertion to fail
2067 | 					},
2068 | 				},
2069 | 			},
2070 | 			expected: "",
2071 | 		},
2072 | 		{
2073 | 			name: "payment with otp_submit action but missing URL field",
2074 | 			payment: map[string]interface{}{
2075 | 				"next": []interface{}{
2076 | 					map[string]interface{}{
2077 | 						"action": "otp_submit",
2078 | 						// no url field
2079 | 					},
2080 | 				},
2081 | 			},
2082 | 			expected: "",
2083 | 		},
2084 | 		{
2085 | 			name: "payment with mixed valid and invalid items in next array",
2086 | 			payment: map[string]interface{}{
2087 | 				"next": []interface{}{
2088 | 					"invalid_item", // This should be skipped
2089 | 					map[string]interface{}{
2090 | 						"action": "redirect",
2091 | 						"url":    "https://example.com/redirect",
2092 | 					},
2093 | 					123, // Another invalid item that should be skipped
2094 | 					map[string]interface{}{
2095 | 						"action": "otp_submit",
2096 | 						"url":    "https://api.razorpay.com/v1/payments/pay_123/otp/submit",
2097 | 					},
2098 | 				},
2099 | 			},
2100 | 			expected: "https://api.razorpay.com/v1/payments/pay_123/otp/submit",
2101 | 		},
2102 | 		{
2103 | 			name: "payment with otp_submit action but missing action field",
2104 | 			payment: map[string]interface{}{
2105 | 				"next": []interface{}{
2106 | 					map[string]interface{}{
2107 | 						// no action field
2108 | 						"url": "https://api.razorpay.com/v1/payments/pay_123/otp/submit",
2109 | 					},
2110 | 				},
2111 | 			},
2112 | 			expected: "",
2113 | 		},
2114 | 	}
2115 | 
2116 | 	for _, tt := range tests {
2117 | 		t.Run(tt.name, func(t *testing.T) {
2118 | 			result := extractOtpSubmitURL(tt.payment)
2119 | 			if result != tt.expected {
2120 | 				t.Errorf("extractOtpSubmitURL() = %v, want %v", result, tt.expected)
2121 | 			}
2122 | 		})
2123 | 	}
2124 | }
2125 | 
2126 | // Test_extractOtpSubmitURL_invalidInput tests the
2127 | // function with invalid input types
2128 | func Test_extractOtpSubmitURL_invalidInput(t *testing.T) {
2129 | 	tests := []struct {
2130 | 		name     string
2131 | 		input    interface{}
2132 | 		expected string
2133 | 	}{
2134 | 		{
2135 | 			name:     "nil input",
2136 | 			input:    nil,
2137 | 			expected: "",
2138 | 		},
2139 | 		{
2140 | 			name:     "string input instead of map",
2141 | 			input:    "invalid_input",
2142 | 			expected: "",
2143 | 		},
2144 | 		{
2145 | 			name:     "integer input instead of map",
2146 | 			input:    123,
2147 | 			expected: "",
2148 | 		},
2149 | 		{
2150 | 			name:     "slice input instead of map",
2151 | 			input:    []string{"invalid"},
2152 | 			expected: "",
2153 | 		},
2154 | 	}
2155 | 
2156 | 	for _, tt := range tests {
2157 | 		t.Run(tt.name, func(t *testing.T) {
2158 | 			result := extractOtpSubmitURL(tt.input)
2159 | 			if result != tt.expected {
2160 | 				t.Errorf("extractOtpSubmitURL() = %v, want %v", result, tt.expected)
2161 | 			}
2162 | 		})
2163 | 	}
2164 | }
2165 | 
2166 | func Test_ResendOtp(t *testing.T) {
2167 | 	resendOtpPathFmt := fmt.Sprintf(
2168 | 		"/%s%s/%%s/otp/resend",
2169 | 		constants.VERSION_V1,
2170 | 		constants.PAYMENT_URL,
2171 | 	)
2172 | 
2173 | 	successResendOtpResp := map[string]interface{}{
2174 | 		"razorpay_payment_id": "pay_MT48CvBhIC98MQ",
2175 | 		"next": []interface{}{
2176 | 			map[string]interface{}{
2177 | 				"action": "otp_submit",
2178 | 				"url": "https://api.razorpay.com/v1/payments/" +
2179 | 					"pay_MT48CvBhIC98MQ/otp/submit",
2180 | 			},
2181 | 		},
2182 | 	}
2183 | 
2184 | 	paymentNotFoundResp := map[string]interface{}{
2185 | 		"error": map[string]interface{}{
2186 | 			"code":        "BAD_REQUEST_ERROR",
2187 | 			"description": "Payment not found",
2188 | 		},
2189 | 	}
2190 | 
2191 | 	tests := []RazorpayToolTestCase{
2192 | 		{
2193 | 			Name: "successful OTP resend",
2194 | 			Request: map[string]interface{}{
2195 | 				"payment_id": "pay_MT48CvBhIC98MQ",
2196 | 			},
2197 | 			MockHttpClient: func() (*http.Client, *httptest.Server) {
2198 | 				return mock.NewHTTPClient(
2199 | 					mock.Endpoint{
2200 | 						Path:     fmt.Sprintf(resendOtpPathFmt, "pay_MT48CvBhIC98MQ"),
2201 | 						Method:   "POST",
2202 | 						Response: successResendOtpResp,
2203 | 					},
2204 | 				)
2205 | 			},
2206 | 			ExpectError: false,
2207 | 			ExpectedResult: map[string]interface{}{
2208 | 				"payment_id": "pay_MT48CvBhIC98MQ",
2209 | 				"status":     "success",
2210 | 				"message": "OTP sent successfully. Please enter the OTP received on your " +
2211 | 					"mobile number to complete the payment.",
2212 | 				"next_step": "Use 'submit_otp' tool with the OTP code received " +
2213 | 					"from user to complete payment authentication.",
2214 | 				"next_tool": "submit_otp",
2215 | 				"next_tool_params": map[string]interface{}{
2216 | 					"payment_id": "pay_MT48CvBhIC98MQ",
2217 | 					"otp_string": "{OTP_CODE_FROM_USER}",
2218 | 				},
2219 | 				"otp_submit_url": "https://api.razorpay.com/v1/payments/" +
2220 | 					"pay_MT48CvBhIC98MQ/otp/submit",
2221 | 				"response_data": successResendOtpResp,
2222 | 			},
2223 | 		},
2224 | 		{
2225 | 			Name: "payment not found for OTP resend",
2226 | 			Request: map[string]interface{}{
2227 | 				"payment_id": "pay_invalid",
2228 | 			},
2229 | 			MockHttpClient: func() (*http.Client, *httptest.Server) {
2230 | 				return mock.NewHTTPClient(
2231 | 					mock.Endpoint{
2232 | 						Path:     fmt.Sprintf(resendOtpPathFmt, "pay_invalid"),
2233 | 						Method:   "POST",
2234 | 						Response: paymentNotFoundResp,
2235 | 					},
2236 | 				)
2237 | 			},
2238 | 			ExpectError:    true,
2239 | 			ExpectedErrMsg: "OTP resend failed: Payment not found",
2240 | 		},
2241 | 		{
2242 | 			Name:    "missing payment_id parameter for resend",
2243 | 			Request: map[string]interface{}{
2244 | 				// No payment_id provided
2245 | 			},
2246 | 			MockHttpClient: nil, // No HTTP client needed for validation error
2247 | 			ExpectError:    true,
2248 | 			ExpectedErrMsg: "missing required parameter: payment_id",
2249 | 		},
2250 | 		{
2251 | 			Name: "OTP resend without next actions",
2252 | 			Request: map[string]interface{}{
2253 | 				"payment_id": "pay_MT48CvBhIC98MQ",
2254 | 			},
2255 | 			MockHttpClient: func() (*http.Client, *httptest.Server) {
2256 | 				return mock.NewHTTPClient(
2257 | 					mock.Endpoint{
2258 | 						Path:   fmt.Sprintf(resendOtpPathFmt, "pay_MT48CvBhIC98MQ"),
2259 | 						Method: "POST",
2260 | 						Response: map[string]interface{}{
2261 | 							"razorpay_payment_id": "pay_MT48CvBhIC98MQ",
2262 | 							"status":              "created",
2263 | 						},
2264 | 					},
2265 | 				)
2266 | 			},
2267 | 			ExpectError: false,
2268 | 			ExpectedResult: map[string]interface{}{
2269 | 				"payment_id": "pay_MT48CvBhIC98MQ",
2270 | 				"status":     "success",
2271 | 				"message": "OTP sent successfully. Please enter the OTP received on your " +
2272 | 					"mobile number to complete the payment.",
2273 | 				"next_step": "Use 'submit_otp' tool with the OTP code received " +
2274 | 					"from user to complete payment authentication.",
2275 | 				"next_tool": "submit_otp",
2276 | 				"next_tool_params": map[string]interface{}{
2277 | 					"payment_id": "pay_MT48CvBhIC98MQ",
2278 | 					"otp_string": "{OTP_CODE_FROM_USER}",
2279 | 				},
2280 | 				"response_data": map[string]interface{}{
2281 | 					"razorpay_payment_id": "pay_MT48CvBhIC98MQ",
2282 | 					"status":              "created",
2283 | 				},
2284 | 			},
2285 | 		},
2286 | 	}
2287 | 
2288 | 	for _, tc := range tests {
2289 | 		t.Run(tc.Name, func(t *testing.T) {
2290 | 			runToolTest(t, tc, ResendOtp, "OTP Resend")
2291 | 		})
2292 | 	}
2293 | }
2294 | 
2295 | // Test_sendOtp_additionalCases tests additional cases for sendOtp function
2296 | func Test_sendOtp_additionalCases(t *testing.T) {
2297 | 	tests := []struct {
2298 | 		name        string
2299 | 		otpURL      string
2300 | 		expectedErr string
2301 | 	}{
2302 | 		{
2303 | 			name: "URL with invalid characters",
2304 | 			otpURL: "https://api.razorpay.com/v1/payments/pay_123/" +
2305 | 				"otp_generate?param=value with spaces",
2306 | 			expectedErr: "OTP generation failed",
2307 | 		},
2308 | 		{
2309 | 			name: "URL with special characters in domain",
2310 | 			otpURL: "https://api-test.razorpay.com/v1/payments/" +
2311 | 				"pay_123/otp_generate",
2312 | 			expectedErr: "OTP generation failed",
2313 | 		},
2314 | 	}
2315 | 
2316 | 	for _, tt := range tests {
2317 | 		t.Run(tt.name, func(t *testing.T) {
2318 | 			err := sendOtp(tt.otpURL)
2319 | 			if err == nil {
2320 | 				t.Error("Expected error but got nil")
2321 | 				return
2322 | 			}
2323 | 			if tt.expectedErr != "" && !strings.Contains(err.Error(), tt.expectedErr) {
2324 | 				t.Errorf(
2325 | 					"Expected error to contain '%s', got '%s'", tt.expectedErr, err.Error(),
2326 | 				)
2327 | 			}
2328 | 		})
2329 | 	}
2330 | }
2331 | 
2332 | // Test_buildPaymentData_edgeCases tests edge cases for
2333 | // buildPaymentData function
2334 | func Test_buildPaymentData_edgeCases(t *testing.T) {
2335 | 	tests := []struct {
2336 | 		name          string
2337 | 		params        map[string]interface{}
2338 | 		currency      string
2339 | 		customerID    string
2340 | 		expectedError string
2341 | 		shouldContain map[string]interface{}
2342 | 	}{
2343 | 		{
2344 | 			name: "payment data with valid customer ID",
2345 | 			params: map[string]interface{}{
2346 | 				"amount":   10000,
2347 | 				"order_id": "order_123",
2348 | 			},
2349 | 			currency:   "INR",
2350 | 			customerID: "cust_123456789",
2351 | 			shouldContain: map[string]interface{}{
2352 | 				"amount":      10000,
2353 | 				"currency":    "INR",
2354 | 				"order_id":    "order_123",
2355 | 				"customer_id": "cust_123456789",
2356 | 			},
2357 | 		},
2358 | 		{
2359 | 			name: "payment data with empty token",
2360 | 			params: map[string]interface{}{
2361 | 				"amount":   10000,
2362 | 				"order_id": "order_123",
2363 | 				"token":    "", // Empty token should not be added
2364 | 			},
2365 | 			currency:   "INR",
2366 | 			customerID: "cust_123456789",
2367 | 			shouldContain: map[string]interface{}{
2368 | 				"amount":      10000,
2369 | 				"currency":    "INR",
2370 | 				"order_id":    "order_123",
2371 | 				"customer_id": "cust_123456789",
2372 | 			},
2373 | 		},
2374 | 		{
2375 | 			name: "payment data with empty customer ID",
2376 | 			params: map[string]interface{}{
2377 | 				"amount":   10000,
2378 | 				"order_id": "order_123",
2379 | 				"token":    "token_123",
2380 | 			},
2381 | 			currency:   "INR",
2382 | 			customerID: "",
2383 | 			shouldContain: map[string]interface{}{
2384 | 				"amount":   10000,
2385 | 				"currency": "INR",
2386 | 				"order_id": "order_123",
2387 | 				"token":    "token_123",
2388 | 			},
2389 | 		},
2390 | 		{
2391 | 			name: "payment data with all parameters",
2392 | 			params: map[string]interface{}{
2393 | 				"amount":   10000,
2394 | 				"order_id": "order_123",
2395 | 				"token":    "token_123",
2396 | 				"email":    "[email protected]",
2397 | 				"contact":  "9876543210",
2398 | 				"method":   "upi",
2399 | 				"save":     true,
2400 | 				"upi": map[string]interface{}{
2401 | 					"flow":        "collect",
2402 | 					"expiry_time": "6",
2403 | 					"vpa":         "test@upi",
2404 | 				},
2405 | 			},
2406 | 			currency:   "INR",
2407 | 			customerID: "cust_123456789",
2408 | 			shouldContain: map[string]interface{}{
2409 | 				"amount":      10000,
2410 | 				"currency":    "INR",
2411 | 				"order_id":    "order_123",
2412 | 				"customer_id": "cust_123456789",
2413 | 				"token":       "token_123",
2414 | 				"email":       "[email protected]",
2415 | 				"contact":     "9876543210",
2416 | 				"method":      "upi",
2417 | 				"save":        true,
2418 | 			},
2419 | 		},
2420 | 	}
2421 | 
2422 | 	for _, tt := range tests {
2423 | 		t.Run(tt.name, func(t *testing.T) {
2424 | 			result := buildPaymentData(tt.params, tt.currency, tt.customerID)
2425 | 
2426 | 			if result == nil {
2427 | 				t.Error("Expected result but got nil")
2428 | 				return
2429 | 			}
2430 | 
2431 | 			// Check that all expected fields are present
2432 | 			for key, expectedValue := range tt.shouldContain {
2433 | 				actualValue, exists := (*result)[key]
2434 | 				if !exists {
2435 | 					t.Errorf("Expected key '%s' to exist in result", key)
2436 | 				} else if actualValue != expectedValue {
2437 | 					t.Errorf(
2438 | 						"Expected '%s' to be %v, got %v", key, expectedValue, actualValue,
2439 | 					)
2440 | 				}
2441 | 			}
2442 | 
2443 | 			// Check that empty token is not added
2444 | 			if tt.params["token"] == "" {
2445 | 				if _, exists := (*result)["token"]; exists {
2446 | 					t.Error("Empty token should not be added to payment data")
2447 | 				}
2448 | 			}
2449 | 
2450 | 			// Check that customer_id is not added when customerID is empty
2451 | 			if tt.customerID == "" {
2452 | 				if _, exists := (*result)["customer_id"]; exists {
2453 | 					t.Error("customer_id should not be added when customerID is empty")
2454 | 				}
2455 | 			}
2456 | 
2457 | 		})
2458 | 	}
2459 | }
2460 | 
2461 | // Test_createOrGetCustomer_scenarios tests
2462 | // createOrGetCustomer function scenarios
2463 | func Test_createOrGetCustomer_scenarios(t *testing.T) {
2464 | 	tests := []struct {
2465 | 		name           string
2466 | 		params         map[string]interface{}
2467 | 		mockSetup      func() (*http.Client, *httptest.Server)
2468 | 		expectedError  string
2469 | 		expectedResult map[string]interface{}
2470 | 	}{
2471 | 		{
2472 | 			name: "successful customer creation with contact",
2473 | 			params: map[string]interface{}{
2474 | 				"contact": "9876543210",
2475 | 			},
2476 | 			mockSetup: func() (*http.Client, *httptest.Server) {
2477 | 				return mock.NewHTTPClient(
2478 | 					mock.Endpoint{
2479 | 						Path:   "/v1/customers",
2480 | 						Method: "POST",
2481 | 						Response: map[string]interface{}{
2482 | 							"id":      "cust_123456789",
2483 | 							"contact": "9876543210",
2484 | 							"email":   "[email protected]",
2485 | 						},
2486 | 					},
2487 | 				)
2488 | 			},
2489 | 			expectedResult: map[string]interface{}{
2490 | 				"id":      "cust_123456789",
2491 | 				"contact": "9876543210",
2492 | 				"email":   "[email protected]",
2493 | 			},
2494 | 		},
2495 | 		{
2496 | 			name: "no contact provided - returns nil",
2497 | 			params: map[string]interface{}{
2498 | 				"amount": 10000,
2499 | 			},
2500 | 			mockSetup: func() (*http.Client, *httptest.Server) {
2501 | 				return mock.NewHTTPClient()
2502 | 			},
2503 | 			expectedResult: nil,
2504 | 		},
2505 | 		{
2506 | 			name: "empty contact provided - returns nil",
2507 | 			params: map[string]interface{}{
2508 | 				"contact": "",
2509 | 			},
2510 | 			mockSetup: func() (*http.Client, *httptest.Server) {
2511 | 				return mock.NewHTTPClient()
2512 | 			},
2513 | 			expectedResult: nil,
2514 | 		},
2515 | 		{
2516 | 			name: "customer creation API error",
2517 | 			params: map[string]interface{}{
2518 | 				"contact": "9876543210",
2519 | 			},
2520 | 			mockSetup: func() (*http.Client, *httptest.Server) {
2521 | 				return mock.NewHTTPClient(
2522 | 					mock.Endpoint{
2523 | 						Path:   "/v1/customers",
2524 | 						Method: "POST",
2525 | 						Response: map[string]interface{}{
2526 | 							"error": map[string]interface{}{
2527 | 								"code":        "BAD_REQUEST_ERROR",
2528 | 								"description": "Invalid contact number",
2529 | 							},
2530 | 						},
2531 | 					},
2532 | 				)
2533 | 			},
2534 | 			expectedError: "failed to create/fetch customer with contact 9876543210",
2535 | 		},
2536 | 	}
2537 | 
2538 | 	for _, tt := range tests {
2539 | 		t.Run(tt.name, func(t *testing.T) {
2540 | 			client, server := newMockRzpClient(tt.mockSetup)
2541 | 			if server != nil {
2542 | 				defer server.Close()
2543 | 			}
2544 | 
2545 | 			result, err := createOrGetCustomer(client, tt.params)
2546 | 
2547 | 			if tt.expectedError != "" {
2548 | 				if err == nil {
2549 | 					t.Error("Expected error but got nil")
2550 | 					return
2551 | 				}
2552 | 				if !strings.Contains(err.Error(), tt.expectedError) {
2553 | 					t.Errorf(
2554 | 						"Expected error to contain '%s', got '%s'", tt.expectedError, err.Error(),
2555 | 					)
2556 | 				}
2557 | 			} else {
2558 | 				if err != nil {
2559 | 					t.Errorf("Unexpected error: %v", err)
2560 | 					return
2561 | 				}
2562 | 
2563 | 				switch {
2564 | 				case tt.expectedResult == nil && result != nil:
2565 | 					t.Errorf("Expected nil result but got %v", result)
2566 | 				case tt.expectedResult != nil && result == nil:
2567 | 					t.Error("Expected result but got nil")
2568 | 				case tt.expectedResult != nil && result != nil:
2569 | 					if result["id"] != tt.expectedResult["id"] {
2570 | 						t.Errorf(
2571 | 							"Expected customer ID '%s', got '%s'", tt.expectedResult["id"],
2572 | 							result["id"],
2573 | 						)
2574 | 					}
2575 | 				}
2576 | 			}
2577 | 		})
2578 | 	}
2579 | }
2580 | 
2581 | // Test_processVPAParameters_scenarios tests
2582 | //
2583 | //	processUPIParameters function scenarios
2584 | func Test_processUPIParameters_scenarios(t *testing.T) {
2585 | 	tests := []struct {
2586 | 		name           string
2587 | 		inputParams    map[string]interface{}
2588 | 		expectedParams map[string]interface{}
2589 | 	}{
2590 | 		{
2591 | 			name: "VPA parameter provided - sets UPI parameters",
2592 | 			inputParams: map[string]interface{}{
2593 | 				"amount":   10000,
2594 | 				"order_id": "order_123",
2595 | 				"vpa":      "9876543210@paytm",
2596 | 			},
2597 | 			expectedParams: map[string]interface{}{
2598 | 				"amount":   10000,
2599 | 				"order_id": "order_123",
2600 | 				"vpa":      "9876543210@paytm",
2601 | 				"method":   "upi",
2602 | 				"upi": map[string]interface{}{
2603 | 					"flow":        "collect",
2604 | 					"expiry_time": "6",
2605 | 					"vpa":         "9876543210@paytm",
2606 | 				},
2607 | 			},
2608 | 		},
2609 | 		{
2610 | 			name: "empty VPA parameter - no changes",
2611 | 			inputParams: map[string]interface{}{
2612 | 				"amount":   10000,
2613 | 				"order_id": "order_123",
2614 | 				"vpa":      "",
2615 | 			},
2616 | 			expectedParams: map[string]interface{}{
2617 | 				"amount":   10000,
2618 | 				"order_id": "order_123",
2619 | 				"vpa":      "",
2620 | 			},
2621 | 		},
2622 | 		{
2623 | 			name: "no VPA parameter - no changes",
2624 | 			inputParams: map[string]interface{}{
2625 | 				"amount":   10000,
2626 | 				"order_id": "order_123",
2627 | 			},
2628 | 			expectedParams: map[string]interface{}{
2629 | 				"amount":   10000,
2630 | 				"order_id": "order_123",
2631 | 			},
2632 | 		},
2633 | 		{
2634 | 			name: "UPI intent parameter provided - sets UPI intent parameters",
2635 | 			inputParams: map[string]interface{}{
2636 | 				"amount":     15000,
2637 | 				"order_id":   "order_456",
2638 | 				"upi_intent": true,
2639 | 			},
2640 | 			expectedParams: map[string]interface{}{
2641 | 				"amount":     15000,
2642 | 				"order_id":   "order_456",
2643 | 				"upi_intent": true,
2644 | 				"method":     "upi",
2645 | 				"upi": map[string]interface{}{
2646 | 					"flow": "intent",
2647 | 				},
2648 | 			},
2649 | 		},
2650 | 		{
2651 | 			name: "UPI intent false - no changes",
2652 | 			inputParams: map[string]interface{}{
2653 | 				"amount":     10000,
2654 | 				"order_id":   "order_123",
2655 | 				"upi_intent": false,
2656 | 			},
2657 | 			expectedParams: map[string]interface{}{
2658 | 				"amount":     10000,
2659 | 				"order_id":   "order_123",
2660 | 				"upi_intent": false,
2661 | 			},
2662 | 		},
2663 | 		{
2664 | 			name: "both VPA and UPI intent provided - UPI intent takes precedence",
2665 | 			inputParams: map[string]interface{}{
2666 | 				"amount":     20000,
2667 | 				"order_id":   "order_789",
2668 | 				"vpa":        "test@upi",
2669 | 				"upi_intent": true,
2670 | 			},
2671 | 			expectedParams: map[string]interface{}{
2672 | 				"amount":     20000,
2673 | 				"order_id":   "order_789",
2674 | 				"vpa":        "test@upi",
2675 | 				"upi_intent": true,
2676 | 				"method":     "upi",
2677 | 				"upi": map[string]interface{}{
2678 | 					"flow": "intent",
2679 | 				},
2680 | 			},
2681 | 		},
2682 | 	}
2683 | 
2684 | 	for _, tt := range tests {
2685 | 		t.Run(tt.name, func(t *testing.T) {
2686 | 			params := make(map[string]interface{})
2687 | 			for k, v := range tt.inputParams {
2688 | 				params[k] = v
2689 | 			}
2690 | 
2691 | 			processUPIParameters(params)
2692 | 
2693 | 			for key, expectedValue := range tt.expectedParams {
2694 | 				actualValue, exists := params[key]
2695 | 				if !exists {
2696 | 					t.Errorf("Expected key '%s' to exist in params", key)
2697 | 					continue
2698 | 				}
2699 | 
2700 | 				if key == "upi" {
2701 | 					expectedUPI := expectedValue.(map[string]interface{})
2702 | 					actualUPI, ok := actualValue.(map[string]interface{})
2703 | 					if !ok {
2704 | 						t.Errorf("Expected UPI to be map[string]interface{}, got %T", actualValue)
2705 | 						continue
2706 | 					}
2707 | 					for upiKey, upiValue := range expectedUPI {
2708 | 						if actualUPI[upiKey] != upiValue {
2709 | 							t.Errorf(
2710 | 								"Expected UPI[%s] to be '%v', got '%v'",
2711 | 								upiKey, upiValue, actualUPI[upiKey],
2712 | 							)
2713 | 						}
2714 | 					}
2715 | 				} else if actualValue != expectedValue {
2716 | 					t.Errorf(
2717 | 						"Expected '%s' to be '%v', got '%v'", key, expectedValue, actualValue,
2718 | 					)
2719 | 				}
2720 | 			}
2721 | 		})
2722 | 	}
2723 | }
2724 | 
2725 | // Test_addAdditionalPaymentParameters_scenarios
2726 | // tests addAdditionalPaymentParameters function scenarios
2727 | // Note: method parameter is set internally by VPA processing, not by user input
2728 | func Test_addAdditionalPaymentParameters_scenarios(t *testing.T) {
2729 | 	tests := []struct {
2730 | 		name           string
2731 | 		paymentData    map[string]interface{}
2732 | 		params         map[string]interface{}
2733 | 		expectedResult map[string]interface{}
2734 | 	}{
2735 | 		{
2736 | 			name: "all additional parameters provided (method set internally)",
2737 | 			paymentData: map[string]interface{}{
2738 | 				"amount":   10000,
2739 | 				"currency": "INR",
2740 | 			},
2741 | 			params: map[string]interface{}{
2742 | 				"method": "upi",
2743 | 				"save":   true,
2744 | 				"upi": map[string]interface{}{
2745 | 					"flow": "collect",
2746 | 					"vpa":  "test@upi",
2747 | 				},
2748 | 			},
2749 | 			expectedResult: map[string]interface{}{
2750 | 				"amount":   10000,
2751 | 				"currency": "INR",
2752 | 				"method":   "upi",
2753 | 				"save":     true,
2754 | 				"upi": map[string]interface{}{
2755 | 					"flow": "collect",
2756 | 					"vpa":  "test@upi",
2757 | 				},
2758 | 			},
2759 | 		},
2760 | 		{
2761 | 			name: "empty method parameter - not added (internal processing)",
2762 | 			paymentData: map[string]interface{}{
2763 | 				"amount": 10000,
2764 | 			},
2765 | 			params: map[string]interface{}{
2766 | 				"method": "",
2767 | 				"save":   false,
2768 | 			},
2769 | 			expectedResult: map[string]interface{}{
2770 | 				"amount": 10000,
2771 | 				"save":   false,
2772 | 			},
2773 | 		},
2774 | 		{
2775 | 			name: "nil UPI parameters - not added (method set internally)",
2776 | 			paymentData: map[string]interface{}{
2777 | 				"amount": 10000,
2778 | 			},
2779 | 			params: map[string]interface{}{
2780 | 				"method": "card",
2781 | 				"upi":    nil,
2782 | 			},
2783 | 			expectedResult: map[string]interface{}{
2784 | 				"amount": 10000,
2785 | 				"method": "card",
2786 | 			},
2787 | 		},
2788 | 		{
2789 | 			name: "invalid UPI parameter type - not added (method set internally)",
2790 | 			paymentData: map[string]interface{}{
2791 | 				"amount": 10000,
2792 | 			},
2793 | 			params: map[string]interface{}{
2794 | 				"method": "upi",
2795 | 				"upi":    "invalid_type",
2796 | 			},
2797 | 			expectedResult: map[string]interface{}{
2798 | 				"amount": 10000,
2799 | 				"method": "upi",
2800 | 			},
2801 | 		},
2802 | 		{
2803 | 			name: "recurring parameter provided",
2804 | 			paymentData: map[string]interface{}{
2805 | 				"amount": 10000,
2806 | 			},
2807 | 			params: map[string]interface{}{
2808 | 				"recurring": true,
2809 | 			},
2810 | 			expectedResult: map[string]interface{}{
2811 | 				"amount":    10000,
2812 | 				"recurring": true,
2813 | 			},
2814 | 		},
2815 | 		{
2816 | 			name: "recurring parameter false",
2817 | 			paymentData: map[string]interface{}{
2818 | 				"amount": 10000,
2819 | 			},
2820 | 			params: map[string]interface{}{
2821 | 				"recurring": false,
2822 | 			},
2823 | 			expectedResult: map[string]interface{}{
2824 | 				"amount":    10000,
2825 | 				"recurring": false,
2826 | 			},
2827 | 		},
2828 | 	}
2829 | 
2830 | 	for _, tt := range tests {
2831 | 		t.Run(tt.name, func(t *testing.T) {
2832 | 			paymentData := make(map[string]interface{})
2833 | 			for k, v := range tt.paymentData {
2834 | 				paymentData[k] = v
2835 | 			}
2836 | 
2837 | 			addAdditionalPaymentParameters(paymentData, tt.params)
2838 | 
2839 | 			for key, expectedValue := range tt.expectedResult {
2840 | 				actualValue, exists := paymentData[key]
2841 | 				if !exists {
2842 | 					t.Errorf("Expected key '%s' to exist in paymentData", key)
2843 | 					continue
2844 | 				}
2845 | 
2846 | 				if key == "upi" {
2847 | 					expectedUPI := expectedValue.(map[string]interface{})
2848 | 					actualUPI, ok := actualValue.(map[string]interface{})
2849 | 					if !ok {
2850 | 						t.Errorf("Expected UPI to be map[string]interface{}, got %T", actualValue)
2851 | 						continue
2852 | 					}
2853 | 					for upiKey, upiValue := range expectedUPI {
2854 | 						if actualUPI[upiKey] != upiValue {
2855 | 							t.Errorf(
2856 | 								"Expected UPI[%s] to be '%v', got '%v'", upiKey, upiValue,
2857 | 								actualUPI[upiKey],
2858 | 							)
2859 | 						}
2860 | 					}
2861 | 				} else if actualValue != expectedValue {
2862 | 					t.Errorf(
2863 | 						"Expected '%s' to be '%v', got '%v'", key, expectedValue, actualValue,
2864 | 					)
2865 | 				}
2866 | 			}
2867 | 
2868 | 			// Check that no unexpected keys were added
2869 | 			for key := range paymentData {
2870 | 				if _, expected := tt.expectedResult[key]; !expected {
2871 | 					t.Errorf("Unexpected key '%s' found in paymentData", key)
2872 | 				}
2873 | 			}
2874 | 		})
2875 | 	}
2876 | }
2877 | 
2878 | // Test_addContactAndEmailToPaymentData_scenarios
2879 | // tests addContactAndEmailToPaymentData function scenarios
2880 | func Test_addContactAndEmailToPaymentData_scenarios(t *testing.T) {
2881 | 	tests := []struct {
2882 | 		name           string
2883 | 		paymentData    map[string]interface{}
2884 | 		params         map[string]interface{}
2885 | 		expectedResult map[string]interface{}
2886 | 	}{
2887 | 		{
2888 | 			name: "both contact and email provided",
2889 | 			paymentData: map[string]interface{}{
2890 | 				"amount": 10000,
2891 | 			},
2892 | 			params: map[string]interface{}{
2893 | 				"contact": "9876543210",
2894 | 				"email":   "[email protected]",
2895 | 			},
2896 | 			expectedResult: map[string]interface{}{
2897 | 				"amount":  10000,
2898 | 				"contact": "9876543210",
2899 | 				"email":   "[email protected]",
2900 | 			},
2901 | 		},
2902 | 		{
2903 | 			name: "only contact provided - email generated",
2904 | 			paymentData: map[string]interface{}{
2905 | 				"amount": 10000,
2906 | 			},
2907 | 			params: map[string]interface{}{
2908 | 				"contact": "9876543210",
2909 | 			},
2910 | 			expectedResult: map[string]interface{}{
2911 | 				"amount":  10000,
2912 | 				"contact": "9876543210",
2913 | 				"email":   "[email protected]",
2914 | 			},
2915 | 		},
2916 | 		{
2917 | 			name: "empty contact and email - nothing added",
2918 | 			paymentData: map[string]interface{}{
2919 | 				"amount": 10000,
2920 | 			},
2921 | 			params: map[string]interface{}{
2922 | 				"contact": "",
2923 | 				"email":   "",
2924 | 			},
2925 | 			expectedResult: map[string]interface{}{
2926 | 				"amount": 10000,
2927 | 			},
2928 | 		},
2929 | 		{
2930 | 			name: "contact provided but email is empty - email generated",
2931 | 			paymentData: map[string]interface{}{
2932 | 				"amount": 10000,
2933 | 			},
2934 | 			params: map[string]interface{}{
2935 | 				"contact": "9876543210",
2936 | 				"email":   "",
2937 | 			},
2938 | 			expectedResult: map[string]interface{}{
2939 | 				"amount":  10000,
2940 | 				"contact": "9876543210",
2941 | 				"email":   "[email protected]",
2942 | 			},
2943 | 		},
2944 | 	}
2945 | 
2946 | 	for _, tt := range tests {
2947 | 		t.Run(tt.name, func(t *testing.T) {
2948 | 			paymentData := make(map[string]interface{})
2949 | 			for k, v := range tt.paymentData {
2950 | 				paymentData[k] = v
2951 | 			}
2952 | 
2953 | 			addContactAndEmailToPaymentData(paymentData, tt.params)
2954 | 
2955 | 			for key, expectedValue := range tt.expectedResult {
2956 | 				actualValue, exists := paymentData[key]
2957 | 				if !exists {
2958 | 					t.Errorf("Expected key '%s' to exist in paymentData", key)
2959 | 					continue
2960 | 				}
2961 | 				if actualValue != expectedValue {
2962 | 					t.Errorf(
2963 | 						"Expected '%s' to be '%v', got '%v'", key, expectedValue, actualValue,
2964 | 					)
2965 | 				}
2966 | 			}
2967 | 
2968 | 			// Check that no unexpected keys were added
2969 | 			for key := range paymentData {
2970 | 				if _, expected := tt.expectedResult[key]; !expected {
2971 | 					t.Errorf("Unexpected key '%s' found in paymentData", key)
2972 | 				}
2973 | 			}
2974 | 		})
2975 | 	}
2976 | }
2977 | 
2978 | // Test_processPaymentResult_edgeCases
2979 | // tests edge cases for processPaymentResult function
2980 | func Test_processPaymentResult_edgeCases(t *testing.T) {
2981 | 	tests := []struct {
2982 | 		name          string
2983 | 		payment       map[string]interface{}
2984 | 		expectedError string
2985 | 		shouldProcess bool
2986 | 	}{
2987 | 		{
2988 | 			name: "payment with OTP URL that causes sendOtp to fail",
2989 | 			payment: map[string]interface{}{
2990 | 				"razorpay_payment_id": "pay_123456789",
2991 | 				"next": []interface{}{
2992 | 					map[string]interface{}{
2993 | 						"action": "otp_generate",
2994 | 						"url":    "http://invalid-url", // Invalid URL
2995 | 					},
2996 | 				},
2997 | 			},
2998 | 			expectedError: "OTP generation failed",
2999 | 		},
3000 | 		{
3001 | 			name: "payment with empty OTP URL",
3002 | 			payment: map[string]interface{}{
3003 | 				"razorpay_payment_id": "pay_123456789",
3004 | 				"next": []interface{}{
3005 | 					map[string]interface{}{
3006 | 						"action": "otp_generate",
3007 | 						"url":    "", // Empty URL should not trigger sendOtp
3008 | 					},
3009 | 				},
3010 | 			},
3011 | 			shouldProcess: true,
3012 | 		},
3013 | 		{
3014 | 			name: "payment without next actions",
3015 | 			payment: map[string]interface{}{
3016 | 				"razorpay_payment_id": "pay_123456789",
3017 | 			},
3018 | 			shouldProcess: true,
3019 | 		},
3020 | 	}
3021 | 
3022 | 	for _, tt := range tests {
3023 | 		t.Run(tt.name, func(t *testing.T) {
3024 | 			result, err := processPaymentResult(tt.payment)
3025 | 
3026 | 			if tt.expectedError != "" {
3027 | 				if err == nil {
3028 | 					t.Error("Expected error but got nil")
3029 | 					return
3030 | 				}
3031 | 				if !strings.Contains(err.Error(), tt.expectedError) {
3032 | 					t.Errorf(
3033 | 						"Expected error to contain '%s', got '%s'", tt.expectedError, err.Error())
3034 | 				}
3035 | 			} else if tt.shouldProcess {
3036 | 				if err != nil {
3037 | 					t.Errorf("Unexpected error: %v", err)
3038 | 				}
3039 | 				if result == nil {
3040 | 					t.Error("Expected result but got nil")
3041 | 				} else {
3042 | 					// Verify the response structure
3043 | 					if paymentID, exists := result["razorpay_payment_id"]; !exists ||
3044 | 						paymentID == "" {
3045 | 						t.Error("Expected razorpay_payment_id in result")
3046 | 					}
3047 | 					if status, exists := result["status"]; !exists ||
3048 | 						status != "payment_initiated" {
3049 | 						t.Error("Expected status to be 'payment_initiated'")
3050 | 					}
3051 | 				}
3052 | 			}
3053 | 		})
3054 | 	}
3055 | }
3056 | 
3057 | // Test for sendOtp function - comprehensive coverage
3058 | func TestSendOtp(t *testing.T) {
3059 | 	t.Run("empty OTP URL", func(t *testing.T) {
3060 | 		err := sendOtp("")
3061 | 		if err == nil {
3062 | 			t.Error("Expected error for empty OTP URL")
3063 | 		}
3064 | 		if err.Error() != "OTP URL is empty" {
3065 | 			t.Errorf("Expected 'OTP URL is empty', got '%s'", err.Error())
3066 | 		}
3067 | 	})
3068 | 
3069 | 	t.Run("invalid URL format", func(t *testing.T) {
3070 | 		err := sendOtp("invalid-url")
3071 | 		if err == nil {
3072 | 			t.Error("Expected error for invalid URL")
3073 | 		}
3074 | 		// The URL parsing succeeds but fails on HTTPS check
3075 | 		if !strings.Contains(err.Error(), "OTP URL must use HTTPS") {
3076 | 			t.Errorf("Expected 'OTP URL must use HTTPS' error, got '%s'", err.Error())
3077 | 		}
3078 | 	})
3079 | 
3080 | 	t.Run("non-HTTPS URL", func(t *testing.T) {
3081 | 		err := sendOtp("http://api.razorpay.com/v1/payments/otp")
3082 | 		if err == nil {
3083 | 			t.Error("Expected error for non-HTTPS URL")
3084 | 		}
3085 | 		if err.Error() != "OTP URL must use HTTPS" {
3086 | 			t.Errorf("Expected 'OTP URL must use HTTPS', got '%s'", err.Error())
3087 | 		}
3088 | 	})
3089 | 
3090 | 	t.Run("non-Razorpay domain", func(t *testing.T) {
3091 | 		err := sendOtp("https://example.com/otp")
3092 | 		if err == nil {
3093 | 			t.Error("Expected error for non-Razorpay domain")
3094 | 		}
3095 | 		if err.Error() != "OTP URL must be from Razorpay domain" {
3096 | 			t.Errorf("Expected 'OTP URL must be from Razorpay domain', got '%s'",
3097 | 				err.Error())
3098 | 		}
3099 | 	})
3100 | 
3101 | 	t.Run("successful OTP request", func(t *testing.T) {
3102 | 		// Since we can't actually call external APIs in tests, we'll test the
3103 | 		// validation logic by testing with a URL that would fail at HTTP call stage
3104 | 		err := sendOtp(
3105 | 			"https://api.razorpay.com/v1/payments/invalid-endpoint-for-test")
3106 | 		if err == nil {
3107 | 			t.Error("Expected error for invalid endpoint")
3108 | 		}
3109 | 		// This should fail at the HTTP request stage, which is expected
3110 | 		if !strings.Contains(err.Error(), "OTP generation failed") {
3111 | 			t.Logf("Got expected error: %s", err.Error())
3112 | 		}
3113 | 	})
3114 | 
3115 | 	t.Run("HTTP request creation failure", func(t *testing.T) {
3116 | 		// Test with invalid characters that would cause http.NewRequest to fail
3117 | 		// This is difficult to trigger in practice, so we'll test URL validation
3118 | 		err := sendOtp("https://api.razorpay.com/v1/payments\x00/otp")
3119 | 		if err == nil {
3120 | 			t.Error("Expected error for invalid URL characters")
3121 | 		}
3122 | 	})
3123 | }
3124 | 
3125 | // Test for extractPaymentID function
3126 | func TestExtractPaymentID(t *testing.T) {
3127 | 	t.Run("payment ID exists", func(t *testing.T) {
3128 | 		payment := map[string]interface{}{
3129 | 			"razorpay_payment_id": "pay_test123",
3130 | 			"other_field":         "value",
3131 | 		}
3132 | 		result := extractPaymentID(payment)
3133 | 		if result != "pay_test123" {
3134 | 			t.Errorf("Expected 'pay_test123', got '%s'", result)
3135 | 		}
3136 | 	})
3137 | 
3138 | 	t.Run("payment ID missing", func(t *testing.T) {
3139 | 		payment := map[string]interface{}{
3140 | 			"other_field": "value",
3141 | 		}
3142 | 		result := extractPaymentID(payment)
3143 | 		if result != "" {
3144 | 			t.Errorf("Expected empty string, got '%s'", result)
3145 | 		}
3146 | 	})
3147 | 
3148 | 	t.Run("payment ID is nil", func(t *testing.T) {
3149 | 		payment := map[string]interface{}{
3150 | 			"razorpay_payment_id": nil,
3151 | 		}
3152 | 		result := extractPaymentID(payment)
3153 | 		if result != "" {
3154 | 			t.Errorf("Expected empty string, got '%s'", result)
3155 | 		}
3156 | 	})
3157 | }
3158 | 
3159 | // Test for extractNextActions function
3160 | func TestExtractNextActions(t *testing.T) {
3161 | 	t.Run("next actions exist", func(t *testing.T) {
3162 | 		payment := map[string]interface{}{
3163 | 			"next": []interface{}{
3164 | 				map[string]interface{}{
3165 | 					"action": "redirect",
3166 | 					"url":    "https://example.com",
3167 | 				},
3168 | 				map[string]interface{}{
3169 | 					"action": "otp",
3170 | 					"url":    "https://otp.example.com",
3171 | 				},
3172 | 			},
3173 | 		}
3174 | 		result := extractNextActions(payment)
3175 | 		if len(result) != 2 {
3176 | 			t.Errorf("Expected 2 actions, got %d", len(result))
3177 | 		}
3178 | 		if result[0]["action"] != "redirect" {
3179 | 			t.Errorf("Expected first action to be 'redirect', got '%s'",
3180 | 				result[0]["action"])
3181 | 		}
3182 | 	})
3183 | 
3184 | 	t.Run("next field missing", func(t *testing.T) {
3185 | 		payment := map[string]interface{}{
3186 | 			"other_field": "value",
3187 | 		}
3188 | 		result := extractNextActions(payment)
3189 | 		if len(result) != 0 {
3190 | 			t.Errorf("Expected empty slice, got %d actions", len(result))
3191 | 		}
3192 | 	})
3193 | 
3194 | 	t.Run("next field is nil", func(t *testing.T) {
3195 | 		payment := map[string]interface{}{
3196 | 			"next": nil,
3197 | 		}
3198 | 		result := extractNextActions(payment)
3199 | 		if len(result) != 0 {
3200 | 			t.Errorf("Expected empty slice, got %d actions", len(result))
3201 | 		}
3202 | 	})
3203 | 
3204 | 	t.Run("next field is not a slice", func(t *testing.T) {
3205 | 		payment := map[string]interface{}{
3206 | 			"next": "invalid_type",
3207 | 		}
3208 | 		result := extractNextActions(payment)
3209 | 		if len(result) != 0 {
3210 | 			t.Errorf("Expected empty slice, got %d actions", len(result))
3211 | 		}
3212 | 	})
3213 | 
3214 | 	t.Run("next field contains non-map items", func(t *testing.T) {
3215 | 		payment := map[string]interface{}{
3216 | 			"next": []interface{}{
3217 | 				"invalid_item",
3218 | 				map[string]interface{}{
3219 | 					"action": "valid_action",
3220 | 				},
3221 | 			},
3222 | 		}
3223 | 		result := extractNextActions(payment)
3224 | 		if len(result) != 1 {
3225 | 			t.Errorf("Expected 1 valid action, got %d", len(result))
3226 | 		}
3227 | 		if result[0]["action"] != "valid_action" {
3228 | 			t.Errorf("Expected action to be 'valid_action', got '%s'",
3229 | 				result[0]["action"])
3230 | 		}
3231 | 	})
3232 | }
3233 | 
3234 | // Test for addNextStepInstructions function
3235 | func TestAddNextStepInstructions(t *testing.T) {
3236 | 	t.Run("add next step instructions with payment ID", func(t *testing.T) {
3237 | 		result := make(map[string]interface{})
3238 | 		paymentID := "pay_test123"
3239 | 
3240 | 		addNextStepInstructions(result, paymentID)
3241 | 
3242 | 		nextStep, exists := result["next_step"]
3243 | 		if !exists {
3244 | 			t.Error("Expected next_step to be added")
3245 | 		}
3246 | 
3247 | 		nextStepStr, ok := nextStep.(string)
3248 | 		if !ok {
3249 | 			t.Error("Expected next_step to be a string")
3250 | 		}
3251 | 
3252 | 		if !strings.Contains(nextStepStr, "resend_otp") {
3253 | 			t.Error("Expected instructions to contain 'resend_otp'")
3254 | 		}
3255 | 		if !strings.Contains(nextStepStr, "submit_otp") {
3256 | 			t.Error("Expected instructions to contain 'submit_otp'")
3257 | 		}
3258 | 
3259 | 		// Check next_tool
3260 | 		nextTool, exists := result["next_tool"]
3261 | 		if !exists {
3262 | 			t.Error("Expected next_tool to be added")
3263 | 		}
3264 | 		if nextTool != "resend_otp" {
3265 | 			t.Errorf("Expected next_tool to be 'resend_otp', got '%s'", nextTool)
3266 | 		}
3267 | 
3268 | 		// Check next_tool_params
3269 | 		params, exists := result["next_tool_params"]
3270 | 		if !exists {
3271 | 			t.Error("Expected next_tool_params to be added")
3272 | 		}
3273 | 		paramsMap, ok := params.(map[string]interface{})
3274 | 		if !ok {
3275 | 			t.Error("Expected next_tool_params to be a map")
3276 | 		}
3277 | 		if paramsMap["payment_id"] != paymentID {
3278 | 			t.Errorf("Expected payment_id to be '%s', got '%s'",
3279 | 				paymentID, paramsMap["payment_id"])
3280 | 		}
3281 | 	})
3282 | 
3283 | 	t.Run("empty payment ID", func(t *testing.T) {
3284 | 		result := make(map[string]interface{})
3285 | 
3286 | 		addNextStepInstructions(result, "")
3287 | 
3288 | 		// Should not add anything when payment ID is empty
3289 | 		if len(result) != 0 {
3290 | 			t.Error("Expected no fields to be added for empty payment ID")
3291 | 		}
3292 | 	})
3293 | }
3294 | 
3295 | // Test for processUPIParameters function
3296 | func TestProcessUPIParameters(t *testing.T) {
3297 | 	t.Run("processUPIParameters", func(t *testing.T) {
3298 | 		// Test with VPA
3299 | 		params := map[string]interface{}{
3300 | 			"vpa": "test@upi",
3301 | 		}
3302 | 		processUPIParameters(params)
3303 | 
3304 | 		if params["method"] != "upi" {
3305 | 			t.Errorf("Expected method to be 'upi', got '%s'", params["method"])
3306 | 		}
3307 | 
3308 | 		upi, exists := params["upi"]
3309 | 		if !exists {
3310 | 			t.Error("Expected upi field to be added")
3311 | 		}
3312 | 
3313 | 		upiMap, ok := upi.(map[string]interface{})
3314 | 		if !ok {
3315 | 			t.Error("Expected upi to be a map")
3316 | 		}
3317 | 
3318 | 		if upiMap["vpa"] != "test@upi" {
3319 | 			t.Errorf("Expected vpa to be 'test@upi', got '%s'", upiMap["vpa"])
3320 | 		}
3321 | 		if upiMap["flow"] != "collect" {
3322 | 			t.Errorf("Expected flow to be 'collect', got '%s'", upiMap["flow"])
3323 | 		}
3324 | 	})
3325 | 
3326 | 	t.Run("processUPIParameters - UPI intent", func(t *testing.T) {
3327 | 		// Test with UPI intent
3328 | 		params := map[string]interface{}{
3329 | 			"upi_intent": true,
3330 | 		}
3331 | 		processUPIParameters(params)
3332 | 
3333 | 		if params["method"] != "upi" {
3334 | 			t.Errorf("Expected method to be 'upi', got '%s'", params["method"])
3335 | 		}
3336 | 
3337 | 		upi, exists := params["upi"]
3338 | 		if !exists {
3339 | 			t.Error("Expected upi field to be added")
3340 | 		}
3341 | 
3342 | 		upiMap, ok := upi.(map[string]interface{})
3343 | 		if !ok {
3344 | 			t.Error("Expected upi to be a map")
3345 | 		}
3346 | 
3347 | 		if upiMap["flow"] != "intent" {
3348 | 			t.Errorf("Expected flow to be 'intent', got '%s'", upiMap["flow"])
3349 | 		}
3350 | 	})
3351 | 
3352 | 	t.Run("processUPIParameters - no UPI params", func(t *testing.T) {
3353 | 		// Test with no UPI parameters
3354 | 		params := map[string]interface{}{
3355 | 			"amount": 1000,
3356 | 		}
3357 | 		processUPIParameters(params)
3358 | 
3359 | 		// Should not modify params when no UPI parameters are present
3360 | 		if _, exists := params["method"]; exists {
3361 | 			t.Error("Expected method not to be added when no UPI params")
3362 | 		}
3363 | 		if _, exists := params["upi"]; exists {
3364 | 			t.Error("Expected upi not to be added when no UPI params")
3365 | 		}
3366 | 	})
3367 | 
3368 | }
3369 | 
3370 | // Test for createOrGetCustomer function
3371 | func TestCreateOrGetCustomer(t *testing.T) {
3372 | 	t.Run("createOrGetCustomer - no contact", func(t *testing.T) {
3373 | 		// Test with no contact parameter
3374 | 		params := map[string]interface{}{
3375 | 			"amount": 1000,
3376 | 		}
3377 | 
3378 | 		// This should return nil, nil since no contact is provided
3379 | 		result, err := createOrGetCustomer(nil, params)
3380 | 
3381 | 		if result != nil {
3382 | 			t.Error("Expected nil result when no contact provided")
3383 | 		}
3384 | 		if err != nil {
3385 | 			t.Errorf("Expected no error when no contact provided, got %v", err)
3386 | 		}
3387 | 	})
3388 | }
3389 | 
3390 | // Test for buildPaymentData function
3391 | func TestBuildPaymentData(t *testing.T) {
3392 | 	t.Run("buildPaymentData", func(t *testing.T) {
3393 | 		params := map[string]interface{}{
3394 | 			"amount":   1000,
3395 | 			"order_id": "order_test123",
3396 | 		}
3397 | 		currency := "INR"
3398 | 		customerId := "cust_test123"
3399 | 
3400 | 		result := buildPaymentData(params, currency, customerId)
3401 | 
3402 | 		if (*result)["amount"] != 1000 {
3403 | 			t.Errorf("Expected amount to be 1000, got %v", (*result)["amount"])
3404 | 		}
3405 | 		if (*result)["currency"] != "INR" {
3406 | 			t.Errorf("Expected currency to be 'INR', got '%s'", (*result)["currency"])
3407 | 		}
3408 | 		if (*result)["customer_id"] != customerId {
3409 | 			t.Errorf("Expected customer_id to be '%s', got '%s'",
3410 | 				customerId, (*result)["customer_id"])
3411 | 		}
3412 | 	})
3413 | 
3414 | 	t.Run("buildPaymentData - no customer ID", func(t *testing.T) {
3415 | 		params := map[string]interface{}{
3416 | 			"amount":   1000,
3417 | 			"order_id": "order_test123",
3418 | 		}
3419 | 		currency := "INR"
3420 | 		customerId := ""
3421 | 
3422 | 		result := buildPaymentData(params, currency, customerId)
3423 | 
3424 | 		if (*result)["amount"] != 1000 {
3425 | 			t.Errorf("Expected amount to be 1000, got %v", (*result)["amount"])
3426 | 		}
3427 | 		if (*result)["currency"] != "INR" {
3428 | 			t.Errorf("Expected currency to be 'INR', got '%s'", (*result)["currency"])
3429 | 		}
3430 | 		// Should not have customer_id when empty
3431 | 		if _, exists := (*result)["customer_id"]; exists {
3432 | 			t.Error("Expected no customer_id when empty string provided")
3433 | 		}
3434 | 	})
3435 | 
3436 | }
3437 | 
3438 | // Test for processPaymentResult function
3439 | func TestProcessPaymentResult(t *testing.T) {
3440 | 	t.Run("processPaymentResult", func(t *testing.T) {
3441 | 		paymentResult := map[string]interface{}{
3442 | 			"razorpay_payment_id": "pay_test123",
3443 | 			"status":              "created",
3444 | 			"next": []interface{}{
3445 | 				map[string]interface{}{
3446 | 					"action": "redirect",
3447 | 					"url":    "https://example.com",
3448 | 				},
3449 | 			},
3450 | 		}
3451 | 
3452 | 		result, err := processPaymentResult(paymentResult)
3453 | 
3454 | 		if err != nil {
3455 | 			t.Errorf("Expected no error, got %v", err)
3456 | 		}
3457 | 
3458 | 		if result["razorpay_payment_id"] != "pay_test123" {
3459 | 			t.Errorf("Expected payment ID, got %v", result["razorpay_payment_id"])
3460 | 		}
3461 | 
3462 | 		if result["status"] != "payment_initiated" {
3463 | 			t.Errorf("Expected status to be 'payment_initiated', got '%s'",
3464 | 				result["status"])
3465 | 		}
3466 | 	})
3467 | 
3468 | 	t.Run("processPaymentResult - with error", func(t *testing.T) {
3469 | 		// Test with payment result that might cause an error
3470 | 		paymentResult := map[string]interface{}{
3471 | 			"error": map[string]interface{}{
3472 | 				"code":        "BAD_REQUEST_ERROR",
3473 | 				"description": "Invalid payment data",
3474 | 			},
3475 | 		}
3476 | 
3477 | 		result, err := processPaymentResult(paymentResult)
3478 | 
3479 | 		// The function should handle this gracefully
3480 | 		if err != nil && result == nil {
3481 | 			t.Logf("Expected behavior - got error: %v", err)
3482 | 		} else if result != nil {
3483 | 			// If no error, result should be properly processed
3484 | 			if result["status"] != "payment_initiated" {
3485 | 				t.Errorf("Expected status to be 'payment_initiated', got '%s'",
3486 | 					result["status"])
3487 | 			}
3488 | 		}
3489 | 	})
3490 | 
3491 | }
3492 | 
3493 | // Test for extractOtpSubmitURL function
3494 | func TestExtractOtpSubmitURL(t *testing.T) {
3495 | 	t.Run("extractOtpSubmitURL", func(t *testing.T) {
3496 | 		// Test with valid response data containing OTP submit URL
3497 | 		responseData := map[string]interface{}{
3498 | 			"next": []interface{}{
3499 | 				map[string]interface{}{
3500 | 					"action": "redirect",
3501 | 					"url":    "https://example.com/redirect",
3502 | 				},
3503 | 				map[string]interface{}{
3504 | 					"action": "otp_submit",
3505 | 					"url":    "https://example.com/otp/submit",
3506 | 				},
3507 | 			},
3508 | 		}
3509 | 
3510 | 		result := extractOtpSubmitURL(responseData)
3511 | 		if result != "https://example.com/otp/submit" {
3512 | 			t.Errorf("Expected OTP submit URL, got '%s'", result)
3513 | 		}
3514 | 	})
3515 | 
3516 | 	t.Run("extractOtpSubmitURL - no OTP action", func(t *testing.T) {
3517 | 		responseData := map[string]interface{}{
3518 | 			"next": []interface{}{
3519 | 				map[string]interface{}{
3520 | 					"action": "redirect",
3521 | 					"url":    "https://example.com/redirect",
3522 | 				},
3523 | 			},
3524 | 		}
3525 | 
3526 | 		result := extractOtpSubmitURL(responseData)
3527 | 		if result != "" {
3528 | 			t.Errorf("Expected empty string, got '%s'", result)
3529 | 		}
3530 | 	})
3531 | 
3532 | 	t.Run("extractOtpSubmitURL - invalid input", func(t *testing.T) {
3533 | 		// Test with invalid input type
3534 | 		result := extractOtpSubmitURL("invalid_input")
3535 | 		if result != "" {
3536 | 			t.Errorf("Expected empty string for invalid input, got '%s'", result)
3537 | 		}
3538 | 	})
3539 | 
3540 | 	t.Run("extractOtpSubmitURL - no next field", func(t *testing.T) {
3541 | 		responseData := map[string]interface{}{
3542 | 			"other_field": "value",
3543 | 		}
3544 | 
3545 | 		result := extractOtpSubmitURL(responseData)
3546 | 		if result != "" {
3547 | 			t.Errorf("Expected empty string when no next field, got '%s'", result)
3548 | 		}
3549 | 	})
3550 | }
3551 | 
3552 | // TestPayments100PercentCoverage_FetchPayment tests FetchPayment coverage
3553 | func TestPayments100PercentCoverage_FetchPayment(t *testing.T) {
3554 | 	// Test FetchPayment with SDK errors
3555 | 	t.Run("FetchPayment - SDK error", func(t *testing.T) {
3556 | 		testCase := RazorpayToolTestCase{
3557 | 			Name: "SDK error",
3558 | 			Request: map[string]interface{}{
3559 | 				"payment_id": "pay_test123",
3560 | 			},
3561 | 			MockHttpClient: func() (*http.Client, *httptest.Server) {
3562 | 				return mock.NewHTTPClient(
3563 | 					mock.Endpoint{
3564 | 						Path:   "/v1/payments/pay_test123",
3565 | 						Method: "GET",
3566 | 						Response: map[string]interface{}{
3567 | 							"error": map[string]interface{}{
3568 | 								"code":        "BAD_REQUEST_ERROR",
3569 | 								"description": "Invalid payment ID",
3570 | 							},
3571 | 						},
3572 | 					},
3573 | 				)
3574 | 			},
3575 | 			ExpectError:    true,
3576 | 			ExpectedErrMsg: "fetching payment failed",
3577 | 		}
3578 | 		runToolTest(t, testCase, FetchPayment, "Payment")
3579 | 	})
3580 | 
3581 | }
3582 | 
3583 | // TestPayments100PercentCoverage_FetchPaymentCardDetails tests
3584 | // FetchPaymentCardDetails coverage
3585 | func TestPayments100PercentCoverage_FetchPaymentCardDetails(t *testing.T) {
3586 | 	// Test FetchPaymentCardDetails with SDK errors
3587 | 	t.Run("FetchPaymentCardDetails - SDK error", func(t *testing.T) {
3588 | 		testCase := RazorpayToolTestCase{
3589 | 			Name: "SDK error",
3590 | 			Request: map[string]interface{}{
3591 | 				"payment_id": "pay_test123",
3592 | 			},
3593 | 			MockHttpClient: func() (*http.Client, *httptest.Server) {
3594 | 				return mock.NewHTTPClient(
3595 | 					mock.Endpoint{
3596 | 						Path:   "/v1/payments/pay_test123/card",
3597 | 						Method: "GET",
3598 | 						Response: map[string]interface{}{
3599 | 							"error": map[string]interface{}{
3600 | 								"code":        "BAD_REQUEST_ERROR",
3601 | 								"description": "Card details not available",
3602 | 							},
3603 | 						},
3604 | 					},
3605 | 				)
3606 | 			},
3607 | 			ExpectError:    true,
3608 | 			ExpectedErrMsg: "fetching card details failed",
3609 | 		}
3610 | 		runToolTest(t, testCase, FetchPaymentCardDetails, "PaymentCardDetails")
3611 | 	})
3612 | 
3613 | }
3614 | 
3615 | // TestPayments100PercentCoverage_UpdatePayment tests UpdatePayment coverage
3616 | func TestPayments100PercentCoverage_UpdatePayment(t *testing.T) {
3617 | 	// Test UpdatePayment with SDK errors
3618 | 	t.Run("UpdatePayment - SDK error", func(t *testing.T) {
3619 | 		testCase := RazorpayToolTestCase{
3620 | 			Name: "SDK error",
3621 | 			Request: map[string]interface{}{
3622 | 				"payment_id": "pay_test123",
3623 | 				"notes": map[string]interface{}{
3624 | 					"key": "value",
3625 | 				},
3626 | 			},
3627 | 			MockHttpClient: func() (*http.Client, *httptest.Server) {
3628 | 				return mock.NewHTTPClient(
3629 | 					mock.Endpoint{
3630 | 						Path:   "/v1/payments/pay_test123",
3631 | 						Method: "PATCH",
3632 | 						Response: map[string]interface{}{
3633 | 							"error": map[string]interface{}{
3634 | 								"code":        "BAD_REQUEST_ERROR",
3635 | 								"description": "Invalid notes",
3636 | 							},
3637 | 						},
3638 | 					},
3639 | 				)
3640 | 			},
3641 | 			ExpectError:    true,
3642 | 			ExpectedErrMsg: "updating payment failed",
3643 | 		}
3644 | 		runToolTest(t, testCase, UpdatePayment, "Payment")
3645 | 	})
3646 | 
3647 | }
3648 | 
3649 | // TestPayments100PercentCoverage_CapturePayment tests CapturePayment coverage
3650 | func TestPayments100PercentCoverage_CapturePayment(t *testing.T) {
3651 | 	// Test CapturePayment with SDK errors
3652 | 	t.Run("CapturePayment - SDK error", func(t *testing.T) {
3653 | 		testCase := RazorpayToolTestCase{
3654 | 			Name: "SDK error",
3655 | 			Request: map[string]interface{}{
3656 | 				"payment_id": "pay_test123",
3657 | 				"amount":     1000,
3658 | 				"currency":   "INR",
3659 | 			},
3660 | 			MockHttpClient: func() (*http.Client, *httptest.Server) {
3661 | 				return mock.NewHTTPClient(
3662 | 					mock.Endpoint{
3663 | 						Path:   "/v1/payments/pay_test123/capture",
3664 | 						Method: "POST",
3665 | 						Response: map[string]interface{}{
3666 | 							"error": map[string]interface{}{
3667 | 								"code":        "BAD_REQUEST_ERROR",
3668 | 								"description": "Payment cannot be captured",
3669 | 							},
3670 | 						},
3671 | 					},
3672 | 				)
3673 | 			},
3674 | 			ExpectError:    true,
3675 | 			ExpectedErrMsg: "capturing payment failed",
3676 | 		}
3677 | 		runToolTest(t, testCase, CapturePayment, "Payment")
3678 | 	})
3679 | 
3680 | }
3681 | 
3682 | // TestPayments100PercentCoverage_FetchAllPayments tests
3683 | // FetchAllPayments coverage
3684 | func TestPayments100PercentCoverage_FetchAllPayments(t *testing.T) {
3685 | 	// Test FetchAllPayments with SDK errors
3686 | 	t.Run("FetchAllPayments - SDK error", func(t *testing.T) {
3687 | 		testCase := RazorpayToolTestCase{
3688 | 			Name: "SDK error",
3689 | 			Request: map[string]interface{}{
3690 | 				"count": 10,
3691 | 			},
3692 | 			MockHttpClient: func() (*http.Client, *httptest.Server) {
3693 | 				return mock.NewHTTPClient(
3694 | 					mock.Endpoint{
3695 | 						Path:   "/v1/payments",
3696 | 						Method: "GET",
3697 | 						Response: map[string]interface{}{
3698 | 							"error": map[string]interface{}{
3699 | 								"code":        "BAD_REQUEST_ERROR",
3700 | 								"description": "Invalid request",
3701 | 							},
3702 | 						},
3703 | 					},
3704 | 				)
3705 | 			},
3706 | 			ExpectError:    true,
3707 | 			ExpectedErrMsg: "fetching payments failed",
3708 | 		}
3709 | 		runToolTest(t, testCase, FetchAllPayments, "Collection")
3710 | 	})
3711 | 
3712 | }
3713 | 
3714 | // TestPayments100PercentCoverage_ResendOtp tests ResendOtp coverage
3715 | func TestPayments100PercentCoverage_ResendOtp(t *testing.T) {
3716 | 	// Test ResendOtp with SDK errors
3717 | 	t.Run("ResendOtp - SDK error", func(t *testing.T) {
3718 | 		testCase := RazorpayToolTestCase{
3719 | 			Name: "SDK error",
3720 | 			Request: map[string]interface{}{
3721 | 				"payment_id": "pay_test123",
3722 | 			},
3723 | 			MockHttpClient: func() (*http.Client, *httptest.Server) {
3724 | 				return mock.NewHTTPClient(
3725 | 					mock.Endpoint{
3726 | 						Path:   "/v1/payments/pay_test123/otp/resend",
3727 | 						Method: "POST",
3728 | 						Response: map[string]interface{}{
3729 | 							"error": map[string]interface{}{
3730 | 								"code":        "BAD_REQUEST_ERROR",
3731 | 								"description": "Cannot resend OTP",
3732 | 							},
3733 | 						},
3734 | 					},
3735 | 				)
3736 | 			},
3737 | 			ExpectError:    true,
3738 | 			ExpectedErrMsg: "OTP resend failed",
3739 | 		}
3740 | 		runToolTest(t, testCase, ResendOtp, "ResendOtp")
3741 | 	})
3742 | 
3743 | }
3744 | 
3745 | // TestPayments100PercentCoverage_SubmitOtp tests SubmitOtp coverage
3746 | func TestPayments100PercentCoverage_SubmitOtp(t *testing.T) {
3747 | 	// Test SubmitOtp with SDK errors
3748 | 	t.Run("SubmitOtp - SDK error", func(t *testing.T) {
3749 | 		testCase := RazorpayToolTestCase{
3750 | 			Name: "SDK error",
3751 | 			Request: map[string]interface{}{
3752 | 				"payment_id": "pay_test123",
3753 | 				"otp_string": "123456",
3754 | 			},
3755 | 			MockHttpClient: func() (*http.Client, *httptest.Server) {
3756 | 				return mock.NewHTTPClient(
3757 | 					mock.Endpoint{
3758 | 						Path:   "/v1/payments/pay_test123/otp/submit",
3759 | 						Method: "POST",
3760 | 						Response: map[string]interface{}{
3761 | 							"error": map[string]interface{}{
3762 | 								"code":        "BAD_REQUEST_ERROR",
3763 | 								"description": "Invalid OTP",
3764 | 							},
3765 | 						},
3766 | 					},
3767 | 				)
3768 | 			},
3769 | 			ExpectError:    true,
3770 | 			ExpectedErrMsg: "OTP verification failed",
3771 | 		}
3772 | 		runToolTest(t, testCase, SubmitOtp, "SubmitOtp")
3773 | 	})
3774 | 
3775 | }
3776 | 
3777 | // TestPayments100PercentCoverage_InitiatePayment tests InitiatePayment coverage
3778 | func TestPayments100PercentCoverage_InitiatePayment(t *testing.T) {
3779 | 
3780 | 	// Test InitiatePayment with errors
3781 | 	t.Run("InitiatePayment - SDK error", func(t *testing.T) {
3782 | 		testCase := RazorpayToolTestCase{
3783 | 			Name: "SDK error",
3784 | 			Request: map[string]interface{}{
3785 | 				"amount":   1000,
3786 | 				"currency": "INR",
3787 | 				"order_id": "order_test123",
3788 | 			},
3789 | 			MockHttpClient: func() (*http.Client, *httptest.Server) {
3790 | 				return mock.NewHTTPClient(
3791 | 					mock.Endpoint{
3792 | 						Path:   "/v1/payments/create/json",
3793 | 						Method: "POST",
3794 | 						Response: map[string]interface{}{
3795 | 							"error": map[string]interface{}{
3796 | 								"code":        "BAD_REQUEST_ERROR",
3797 | 								"description": "Invalid payment data",
3798 | 							},
3799 | 						},
3800 | 					},
3801 | 				)
3802 | 			},
3803 | 			ExpectError:    true,
3804 | 			ExpectedErrMsg: "initiating payment failed",
3805 | 		}
3806 | 		runToolTest(t, testCase, InitiatePayment, "InitiatePayment")
3807 | 	})
3808 | 
3809 | 	// Test sendOtp with HTTP error status
3810 | 	t.Run("sendOtp - HTTP error status", func(t *testing.T) {
3811 | 		server := httptest.NewTLSServer(
3812 | 			http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
3813 | 				w.WriteHeader(http.StatusBadRequest)
3814 | 			}))
3815 | 		defer server.Close()
3816 | 
3817 | 		// Replace domain to pass validation
3818 | 		testURL := strings.Replace(
3819 | 			server.URL, server.URL[8:], "api.razorpay.com/v1/payments/otp", 1)
3820 | 		err := sendOtp(testURL)
3821 | 		if err == nil {
3822 | 			t.Error("Expected error for HTTP error status")
3823 | 		}
3824 | 		if !strings.Contains(err.Error(), "OTP generation failed with HTTP status") {
3825 | 			t.Logf("Got expected error: %s", err.Error())
3826 | 		}
3827 | 	})
3828 | 
3829 | 	// More aggressive tests - hitting every error path!
3830 | 	t.Run("sendOtp - request creation error", func(t *testing.T) {
3831 | 		// Test with malformed URL that passes parsing but fails request creation
3832 | 		err := sendOtp("https://api.razorpay.com:99999/invalid")
3833 | 		if err == nil {
3834 | 			t.Error("Expected error for malformed URL")
3835 | 		}
3836 | 		if !strings.Contains(err.Error(), "failed to create OTP request") &&
3837 | 			!strings.Contains(err.Error(), "OTP generation failed") {
3838 | 			t.Logf("Got expected error: %s", err.Error())
3839 | 		}
3840 | 	})
3841 | 
3842 | 	// Test with extremely long URL to trigger different error paths
3843 | 	t.Run("sendOtp - extreme URL", func(t *testing.T) {
3844 | 		longPath := strings.Repeat("a", 10000)
3845 | 		testURL := "https://api.razorpay.com/v1/payments/" + longPath + "/otp"
3846 | 		err := sendOtp(testURL)
3847 | 		if err == nil {
3848 | 			t.Error("Expected error for extreme URL")
3849 | 		}
3850 | 	})
3851 | 
3852 | }
3853 | 
3854 | // contextKey is a type for context keys to avoid collisions
3855 | type contextKey string
3856 | 
3857 | // TestPayments100PercentCoverage_ContextErrors tests context error paths
3858 | func TestPayments100PercentCoverage_ContextErrors(t *testing.T) {
3859 | 	// Test getClientFromContextOrDefault error path
3860 | 	t.Run("SubmitOtp - client context error", func(t *testing.T) {
3861 | 		// Create context with invalid client
3862 | 		ctx := context.WithValue(
3863 | 			context.Background(), contextKey("invalid_key"), "invalid_value")
3864 | 
3865 | 		tool := SubmitOtp(nil, nil)
3866 | 		request := mcpgo.CallToolRequest{
3867 | 			Arguments: map[string]interface{}{
3868 | 				"payment_id": "pay_test123",
3869 | 				"otp_string": "123456",
3870 | 			},
3871 | 		}
3872 | 
3873 | 		result, err := tool.GetHandler()(ctx, request)
3874 | 		if err != nil {
3875 | 			t.Errorf("Expected no error, got %v", err)
3876 | 		}
3877 | 		if result == nil || !result.IsError {
3878 | 			t.Error("Expected error result for invalid client context")
3879 | 		}
3880 | 	})
3881 | 
3882 | 	// Test sendOtp with actual HTTP client failure
3883 | 	t.Run("sendOtp - HTTP client failure", func(t *testing.T) {
3884 | 		// Test with a URL that will fail at the HTTP client level
3885 | 		err := sendOtp("https://api.razorpay.com:99999/invalid/path/that/will/fail")
3886 | 		if err == nil {
3887 | 			t.Error("Expected error for HTTP client failure")
3888 | 		}
3889 | 		if !strings.Contains(err.Error(), "OTP generation failed") {
3890 | 			t.Logf("Got expected error: %s", err.Error())
3891 | 		}
3892 | 	})
3893 | 
3894 | }
3895 | 
3896 | // TestPayments100PercentCoverage_ContextErrors2 tests more context error paths
3897 | func TestPayments100PercentCoverage_ContextErrors2(t *testing.T) {
3898 | 	// Test InitiatePayment - getClientFromContextOrDefault error
3899 | 	t.Run("InitiatePayment - client context error", func(t *testing.T) {
3900 | 		// Create context without client
3901 | 		ctx := context.Background()
3902 | 
3903 | 		tool := InitiatePayment(nil, nil)
3904 | 		request := mcpgo.CallToolRequest{
3905 | 			Arguments: map[string]interface{}{
3906 | 				"amount":   1000,
3907 | 				"order_id": "order_test123",
3908 | 			},
3909 | 		}
3910 | 
3911 | 		result, err := tool.GetHandler()(ctx, request)
3912 | 		if err != nil {
3913 | 			t.Errorf("Expected no error, got %v", err)
3914 | 		}
3915 | 		if result == nil || !result.IsError {
3916 | 			t.Error("Expected error result for missing client context")
3917 | 		}
3918 | 		if !strings.Contains(result.Text, "no client found in context") {
3919 | 			t.Errorf("Expected 'no client found in context', got '%s'", result.Text)
3920 | 		}
3921 | 	})
3922 | 
3923 | 	// Simple working test
3924 | 	t.Run("InitiatePayment - basic test", func(t *testing.T) {
3925 | 		testCase := RazorpayToolTestCase{
3926 | 			Name: "basic test",
3927 | 			Request: map[string]interface{}{
3928 | 				"amount":   1000,
3929 | 				"order_id": "order_test123",
3930 | 			},
3931 | 			MockHttpClient: func() (*http.Client, *httptest.Server) {
3932 | 				return mock.NewHTTPClient(
3933 | 					mock.Endpoint{
3934 | 						Path:   "/v1/payments/create/json",
3935 | 						Method: "POST",
3936 | 						Response: map[string]interface{}{
3937 | 							"id":         "pay_test123",
3938 | 							"entity":     "payment",
3939 | 							"amount":     1000,
3940 | 							"currency":   "INR",
3941 | 							"status":     "created",
3942 | 							"invalid":    make(chan int), // This causes JSON marshal to fail
3943 | 							"created_at": 1234567890,
3944 | 						},
3945 | 					},
3946 | 				)
3947 | 			},
3948 | 			ExpectError:    true,
3949 | 			ExpectedErrMsg: "failed",
3950 | 		}
3951 | 		runToolTest(t, testCase, InitiatePayment, "Payment")
3952 | 	})
3953 | 
3954 | }
3955 | 
3956 | // TestPayments100PercentCoverage_ContextErrors3 tests remaining
3957 | // context error paths
3958 | func TestPayments100PercentCoverage_ContextErrors3(t *testing.T) {
3959 | 	// Test FetchPayment - getClientFromContextOrDefault error
3960 | 	t.Run("FetchPayment - client context error", func(t *testing.T) {
3961 | 		// Create context without client
3962 | 		ctx := context.Background()
3963 | 
3964 | 		tool := FetchPayment(nil, nil)
3965 | 		request := mcpgo.CallToolRequest{
3966 | 			Arguments: map[string]interface{}{
3967 | 				"payment_id": "pay_test123",
3968 | 			},
3969 | 		}
3970 | 
3971 | 		result, err := tool.GetHandler()(ctx, request)
3972 | 		if err != nil {
3973 | 			t.Errorf("Expected no error, got %v", err)
3974 | 		}
3975 | 		if result == nil || !result.IsError {
3976 | 			t.Error("Expected error result for missing client context")
3977 | 		}
3978 | 		if !strings.Contains(result.Text, "no client found in context") {
3979 | 			t.Errorf("Expected 'no client found in context', got '%s'", result.Text)
3980 | 		}
3981 | 	})
3982 | 
3983 | 	// Test FetchPaymentCardDetails - getClientFromContextOrDefault error
3984 | 	t.Run("FetchPaymentCardDetails - client context error", func(t *testing.T) {
3985 | 		// Create context without client
3986 | 		ctx := context.Background()
3987 | 
3988 | 		tool := FetchPaymentCardDetails(nil, nil)
3989 | 		request := mcpgo.CallToolRequest{
3990 | 			Arguments: map[string]interface{}{
3991 | 				"payment_id": "pay_test123",
3992 | 			},
3993 | 		}
3994 | 
3995 | 		result, err := tool.GetHandler()(ctx, request)
3996 | 		if err != nil {
3997 | 			t.Errorf("Expected no error, got %v", err)
3998 | 		}
3999 | 		if result == nil || !result.IsError {
4000 | 			t.Error("Expected error result for missing client context")
4001 | 		}
4002 | 		if !strings.Contains(result.Text, "no client found in context") {
4003 | 			t.Errorf("Expected 'no client found in context', got '%s'", result.Text)
4004 | 		}
4005 | 	})
4006 | 
4007 | 	// Test FetchPaymentCardDetails - JSON marshal error
4008 | 	// (using channel in response)
4009 | 	t.Run("FetchPaymentCardDetails - JSON marshal error", func(t *testing.T) {
4010 | 		testCase := RazorpayToolTestCase{
4011 | 			Name: "JSON marshal error",
4012 | 			Request: map[string]interface{}{
4013 | 				"payment_id": "pay_test123",
4014 | 			},
4015 | 			MockHttpClient: func() (*http.Client, *httptest.Server) {
4016 | 				return mock.NewHTTPClient(
4017 | 					mock.Endpoint{
4018 | 						Path:   "/v1/payments/pay_test123/card",
4019 | 						Method: "GET",
4020 | 						Response: map[string]interface{}{
4021 | 							"id":         "card_test123",
4022 | 							"entity":     "card",
4023 | 							"name":       "Test User",
4024 | 							"last4":      "1234",
4025 | 							"network":    "Visa",
4026 | 							"type":       "credit",
4027 | 							"invalid":    make(chan int), // This causes JSON marshal to fail
4028 | 							"created_at": 1234567890,
4029 | 						},
4030 | 					},
4031 | 				)
4032 | 			},
4033 | 			ExpectError:    true,
4034 | 			ExpectedErrMsg: "failed",
4035 | 		}
4036 | 		runToolTest(t, testCase, FetchPaymentCardDetails, "Card Details")
4037 | 	})
4038 | 
4039 | }
4040 | 
4041 | // TestPayments100PercentCoverage_ContextErrors4 tests final context error paths
4042 | func TestPayments100PercentCoverage_ContextErrors4(t *testing.T) {
4043 | 	// Test CapturePayment - getClientFromContextOrDefault error
4044 | 	t.Run("CapturePayment - client context error", func(t *testing.T) {
4045 | 		// Create context without client
4046 | 		ctx := context.Background()
4047 | 
4048 | 		tool := CapturePayment(nil, nil)
4049 | 		request := mcpgo.CallToolRequest{
4050 | 			Arguments: map[string]interface{}{
4051 | 				"payment_id": "pay_test123",
4052 | 				"amount":     1000,
4053 | 				"currency":   "INR",
4054 | 			},
4055 | 		}
4056 | 
4057 | 		result, err := tool.GetHandler()(ctx, request)
4058 | 		if err != nil {
4059 | 			t.Errorf("Expected no error, got %v", err)
4060 | 		}
4061 | 		if result == nil || !result.IsError {
4062 | 			t.Error("Expected error result for missing client context")
4063 | 		}
4064 | 		if !strings.Contains(result.Text, "no client found in context") {
4065 | 			t.Errorf("Expected 'no client found in context', got '%s'", result.Text)
4066 | 		}
4067 | 	})
4068 | 
4069 | 	// Test CapturePayment - JSON marshal error (using channel in response)
4070 | 	t.Run("CapturePayment - JSON marshal error", func(t *testing.T) {
4071 | 		testCase := RazorpayToolTestCase{
4072 | 			Name: "JSON marshal error",
4073 | 			Request: map[string]interface{}{
4074 | 				"payment_id": "pay_test123",
4075 | 				"amount":     1000,
4076 | 				"currency":   "INR",
4077 | 			},
4078 | 			MockHttpClient: func() (*http.Client, *httptest.Server) {
4079 | 				return mock.NewHTTPClient(
4080 | 					mock.Endpoint{
4081 | 						Path:   "/v1/payments/pay_test123/capture",
4082 | 						Method: "POST",
4083 | 						Response: map[string]interface{}{
4084 | 							"id":         "pay_test123",
4085 | 							"entity":     "payment",
4086 | 							"amount":     1000,
4087 | 							"currency":   "INR",
4088 | 							"status":     "captured",
4089 | 							"invalid":    make(chan int), // This causes JSON marshal to fail
4090 | 							"created_at": 1234567890,
4091 | 						},
4092 | 					},
4093 | 				)
4094 | 			},
4095 | 			ExpectError:    true,
4096 | 			ExpectedErrMsg: "failed",
4097 | 		}
4098 | 		runToolTest(t, testCase, CapturePayment, "Payment")
4099 | 	})
4100 | 
4101 | 	// Test UpdatePayment - getClientFromContextOrDefault error
4102 | 	t.Run("UpdatePayment - client context error", func(t *testing.T) {
4103 | 		// Create context without client
4104 | 		ctx := context.Background()
4105 | 
4106 | 		tool := UpdatePayment(nil, nil)
4107 | 		request := mcpgo.CallToolRequest{
4108 | 			Arguments: map[string]interface{}{
4109 | 				"payment_id": "pay_test123",
4110 | 				"notes": map[string]interface{}{
4111 | 					"key": "value",
4112 | 				},
4113 | 			},
4114 | 		}
4115 | 
4116 | 		result, err := tool.GetHandler()(ctx, request)
4117 | 		if err != nil {
4118 | 			t.Errorf("Expected no error, got %v", err)
4119 | 		}
4120 | 		if result == nil || !result.IsError {
4121 | 			t.Error("Expected error result for missing client context")
4122 | 		}
4123 | 		if !strings.Contains(result.Text, "no client found in context") {
4124 | 			t.Errorf("Expected 'no client found in context', got '%s'", result.Text)
4125 | 		}
4126 | 	})
4127 | 
4128 | 	// Test UpdatePayment - JSON marshal error (using channel in response)
4129 | 	t.Run("UpdatePayment - JSON marshal error", func(t *testing.T) {
4130 | 		testCase := RazorpayToolTestCase{
4131 | 			Name: "JSON marshal error",
4132 | 			Request: map[string]interface{}{
4133 | 				"payment_id": "pay_test123",
4134 | 				"notes": map[string]interface{}{
4135 | 					"key": "value",
4136 | 				},
4137 | 			},
4138 | 			MockHttpClient: func() (*http.Client, *httptest.Server) {
4139 | 				return mock.NewHTTPClient(
4140 | 					mock.Endpoint{
4141 | 						Path:   "/v1/payments/pay_test123",
4142 | 						Method: "PATCH",
4143 | 						Response: map[string]interface{}{
4144 | 							"id":       "pay_test123",
4145 | 							"entity":   "payment",
4146 | 							"amount":   1000,
4147 | 							"currency": "INR",
4148 | 							"status":   "authorized",
4149 | 							"invalid":  make(chan int), // This causes JSON marshal to fail
4150 | 							"notes": map[string]interface{}{
4151 | 								"key": "value",
4152 | 							},
4153 | 							"created_at": 1234567890,
4154 | 						},
4155 | 					},
4156 | 				)
4157 | 			},
4158 | 			ExpectError:    true,
4159 | 			ExpectedErrMsg: "failed",
4160 | 		}
4161 | 		runToolTest(t, testCase, UpdatePayment, "Payment")
4162 | 	})
4163 | 
4164 | 	// Test FetchAllPayments - getClientFromContextOrDefault error
4165 | 	t.Run("FetchAllPayments - client context error", func(t *testing.T) {
4166 | 		// Create context without client
4167 | 		ctx := context.Background()
4168 | 
4169 | 		tool := FetchAllPayments(nil, nil)
4170 | 		request := mcpgo.CallToolRequest{
4171 | 			Arguments: map[string]interface{}{
4172 | 				"count": 10,
4173 | 			},
4174 | 		}
4175 | 
4176 | 		result, err := tool.GetHandler()(ctx, request)
4177 | 		if err != nil {
4178 | 			t.Errorf("Expected no error, got %v", err)
4179 | 		}
4180 | 		if result == nil || !result.IsError {
4181 | 			t.Error("Expected error result for missing client context")
4182 | 		}
4183 | 		if !strings.Contains(result.Text, "no client found in context") {
4184 | 			t.Errorf("Expected 'no client found in context', got '%s'", result.Text)
4185 | 		}
4186 | 	})
4187 | 
4188 | 	// Test FetchAllPayments - JSON marshal error (using channel in response)
4189 | 	t.Run("FetchAllPayments - JSON marshal error", func(t *testing.T) {
4190 | 		testCase := RazorpayToolTestCase{
4191 | 			Name: "JSON marshal error",
4192 | 			Request: map[string]interface{}{
4193 | 				"count": 10,
4194 | 			},
4195 | 			MockHttpClient: func() (*http.Client, *httptest.Server) {
4196 | 				return mock.NewHTTPClient(
4197 | 					mock.Endpoint{
4198 | 						Path:   "/v1/payments",
4199 | 						Method: "GET",
4200 | 						Response: map[string]interface{}{
4201 | 							"entity": "collection",
4202 | 							"count":  1,
4203 | 							"items": []interface{}{
4204 | 								map[string]interface{}{
4205 | 									"id":         "pay_test123",
4206 | 									"invalid":    make(chan int), // This causes JSON marshal to fail
4207 | 									"entity":     "payment",
4208 | 									"amount":     1000,
4209 | 									"currency":   "INR",
4210 | 									"status":     "created",
4211 | 									"created_at": 1234567890,
4212 | 								},
4213 | 							},
4214 | 						},
4215 | 					},
4216 | 				)
4217 | 			},
4218 | 			ExpectError:    true,
4219 | 			ExpectedErrMsg: "failed",
4220 | 		}
4221 | 		runToolTest(t, testCase, FetchAllPayments, "Collection")
4222 | 	})
4223 | }
4224 | 
```
Page 5/5FirstPrevNextLast