Skip to content

feat(sdks): TS auto-retry with backoff, Go payment links + WaitForPayment and examples, Python mock-server test suite - #1276

Open
JosephOladele wants to merge 4 commits into
MetroLogic:mainfrom
JosephOladele:feat/sdk-retries-go-helpers-python-tests
Open

JosephOladele wants to merge 4 commits into
MetroLogic:mainfrom
JosephOladele:feat/sdk-retries-go-helpers-python-tests

Conversation

@JosephOladele

Copy link
Copy Markdown

This PR covers four SDK issues. Each one has its own commit.

Closes #1265
Closes #1266
Closes #1267
Closes #1268

#1266: TypeScript SDK auto-retry with exponential backoff

  • New client options: maxRetries (default 2; the old retries option still works as a deprecated alias), timeout (per attempt, default 30s, 0 disables), retryInitialDelayMs and retryMaxDelayMs.
  • Retries on 429, 502, 503 and 504, on network errors, and on timeouts. Backoff is exponential with full jitter.
  • On 429 the SDK waits at least as long as Retry-After asks, whether it's given in seconds or as an HTTP date. If the wait would be longer than retryMaxDelayMs, it returns the 429 instead of hanging.
  • Only idempotent requests are retried: GET/PUT/DELETE, plus POST/PATCH when an Idempotency-Key is sent. payments.create(params, { idempotencyKey }) and refunds.create({ idempotency_key }) now send that header. A POST to create a payment without a key is never retried, so a retry can't double-charge.
  • Timeouts use AbortSignal.timeout. If the last attempt times out, the SDK throws FluxaPayError with status 408 and code REQUEST_TIMEOUT.
  • New tests in sdk.test.ts: default and configured retry counts, each retryable status, no retry on 500, POST with and without an idempotency key, the Retry-After cap, Retry-After as an HTTP date, the backoff schedule, timeouts, and timeout: 0.

#1265: Go SDK payment link and polling helpers

  • client.CreatePaymentLink(ctx, *CreatePaymentLinkParams) (*PaymentLink, error), plus client.PaymentLinks.Create/Get/List. Request and response structs have json tags for every field. Parameters are validated client-side first (errors wrap ErrInvalidParams), so bad input never makes a request.
  • client.WaitForPayment(ctx, paymentID, opts ...PollOption) (*Payment, error), also available as client.Payments.WaitForPayment:
    • The first poll happens immediately; after that the interval backs off exponentially. Options: WithPollInterval, WithMaxPollInterval, WithPollBackoff, WithPollTimeout, WithSuccessStatuses, WithFailureStatuses, WithOnPoll.
    • Context cancellation and deadlines are honoured, including during in-flight HTTP calls.
    • Transient errors (429, 5xx, network) keep polling. Permanent errors such as 404 or 401 are returned immediately.
    • A failure status returns *PaymentStatusError, which matches errors.Is(err, ErrPaymentNotSuccessful). A timeout or cancellation returns *WaitTimeoutError, which unwraps to the context error and records the last status seen.
  • httptest tests cover success, already-paid, failure statuses, custom statuses, context timeout, WithPollTimeout, cancellation, transient-error retries, permanent errors, validation and the backoff math.

#1268: Go SDK examples and quickstart

  • examples/checkout_flow: creates a payment, prints checkout_url, waits with WaitForPayment, and exits with a distinct code for success, failure, or timeout/Ctrl+C.
  • examples/webhook_server: a net/http server. It reads the raw body with a size limit, verifies the signature and the 300s timestamp window, dispatches on event type, and shuts down gracefully.
  • examples/payment_links: creates a fixed-amount or open-amount link and lists active links.
  • examples/README.md: setting environment variables on macOS/Linux and PowerShell, run steps for each example, a Gin snippet, and an error-handling cheat sheet. The SDK README is updated too.
  • The examples use only the standard library, so go.mod gets no new dependencies.
  • New go-sdk-ci.yml runs go vet, builds the examples, and runs go test -race on Go SDK changes.

#1267: Python SDK mock server tests

  • tests/conftest.py holds canonical response fixtures (payment, payment link, merchant, refund, error bodies) and a respx router fixture.
  • tests/test_mock_server.py covers:
    • merchants, payments, payment links, refunds and webhooks, on both the sync and async clients
    • a 400/401/404/500 error matrix across 10 endpoints, plus 403/410/422/429/503, non-JSON error bodies and network errors
    • that the API key, standard headers and custom headers are sent on every request
  • SDK changes this needed (all backward compatible):
    • typed exceptions BadRequestError, AuthenticationError, PermissionDeniedError, NotFoundError, ConflictError, RateLimitError (with retry_after) and ServerError, all subclassing FluxaPayError; field errors are exposed on .errors
    • new client.payment_links and client.merchants resources
    • headers= and timeout= client options; custom headers can't override Authorization
    • refunds send idempotency_key as the Idempotency-Key header
  • New python-sdk-ci.yml runs pytest on Python 3.9–3.12 for PRs that change the Python SDK.

All new and changed SDK code is covered by tests, and the existing tests are unchanged.

… POST retries

- maxRetries option (default 2; retries kept as a deprecated alias)
- per-attempt timeout via AbortSignal.timeout (default 30s), surfaced as
  FluxaPayError 408 REQUEST_TIMEOUT
- configurable exponential backoff with full jitter
- Retry-After (seconds or HTTP date) honoured on 429; waits longer than
  retryMaxDelayMs surface the error instead
- POST/PATCH only retried when an Idempotency-Key is sent
  (payments.create options, refunds idempotency_key)
- unit tests for retry counts, statuses, Retry-After and timeouts

Closes MetroLogic#1266
- CreatePaymentLink and PaymentLinks.Create/Get/List with json-tagged
  request/response structs and client-side validation (ErrInvalidParams)
- WaitForPayment with immediate first poll, exponential backoff, poll
  timeout and context cancellation; retries transient errors
- typed errors: PaymentStatusError (ErrPaymentNotSuccessful) and
  WaitTimeoutError (unwraps to the context error)
- httptest coverage for success, error and timeout paths

Closes MetroLogic#1265
- examples/checkout_flow: create a payment and wait for completion
- examples/webhook_server: net/http server verifying webhook signatures
- examples/payment_links: create and list payment links
- examples/README.md with env var setup, run steps and a Gin snippet
- Go SDK CI job that vets, builds the examples and runs tests

Closes MetroLogic#1268
- pytest suite covering merchants, payments, payment links, refunds and
  webhooks (sync and async) with canonical response fixtures
- 200/400/401/404/500 matrix plus 403/410/422/429/503 handling
- typed exceptions (BadRequestError, AuthenticationError, NotFoundError,
  RateLimitError, ServerError, ...) all subclassing FluxaPayError
- payment_links and merchants resources, custom headers and timeout
  client options, refund Idempotency-Key header
- GitHub Actions workflow running pytest on Python SDK changes

Closes MetroLogic#1267
@drips-wave

drips-wave Bot commented Oct 15, 2026

Copy link
Copy Markdown

@JosephOladele Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant