> ## Documentation Index
> Fetch the complete documentation index at: https://doc.gopay.et/llms.txt
> Use this file to discover all available pages before exploring further.

# Testing Guide

> Test your GoPay integration end to end before going live

# Testing Guide

Test everything in the **sandbox environment** with a sandbox API key. Sandbox and production are separate hosts with separate keys — and because the host is part of the signed canonical string, a sandbox signature can never accidentally hit production.

## Before You Start

* Create a **sandbox API key** in the merchant dashboard with the permissions you need (`c2b`, `b2c`, or both)
* Note your sandbox base URL from onboarding
* Have the GoPay **gateway public key** for the sandbox ready for webhook verification

## Testing Checklist

<Steps>
  <Step title="Verify your signature implementation">
    Send a minimal signed request. If you get `INVALID_SIGNATURE`, work through the
    [common mistakes](/getting-started/authentication#common-mistakes) — the usual culprits are
    re-serialized bodies, millisecond timestamps, and signing with the wrong host.
  </Step>

  <Step title="Exercise validation errors">
    Deliberately send a zero amount, an unsupported currency, and an `http://` notify URL.
    You should get `VALIDATION_FAILED` with **all** problems listed in one message.
  </Step>

  <Step title="Test replay protection">
    Send the same request twice with the same `X-Request-ID`. The second attempt must fail
    with `IDEMPOTENCY_CONFLICT` (409). Your retry logic should generate a fresh ID per attempt.
  </Step>

  <Step title="Complete a payment end to end (C2B)">
    Initiate a payment, open the returned `checkoutUrl`, complete the sandbox payment, and
    confirm you receive the `completed` webhook and the customer lands on your `successUrl`.
  </Step>

  <Step title="Complete a transfer end to end (B2C)">
    Initiate a transfer to a sandbox phone number and confirm the settlement webhook arrives.
    Also test with an amount larger than your sandbox wallet balance to exercise
    `INSUFFICIENT_FUNDS` (402).
  </Step>

  <Step title="Verify webhook handling">
    Confirm your endpoint verifies the Ed25519 signature against the raw body, rejects tampered
    payloads with 401, dedupes on `X-Request-ID`, and responds 2xx within 10 seconds.
  </Step>
</Steps>

## Testing Webhooks Locally

Use a tunneling tool such as **ngrok** to expose your local handler over HTTPS:

```bash theme={null}
ngrok http 3000
# use the generated https URL as your notifyUrl in sandbox requests
```

Remember the constraints from the [webhooks guide](/webhooks/index#webhook-requirements): HTTPS only, publicly reachable (no private or loopback addresses), and redirects are not followed.

<Tip>
  To test your handler's rejection path without GoPay, send it a request with a bogus signature —
  a correct implementation answers 401. Only sandbox traffic can exercise the success path,
  because valid signatures require GoPay's gateway private key.
</Tip>

## Go-Live Checklist

* Swap the sandbox base URL, API key, and gateway public key for their production values
* Confirm your production `notifyUrl` is HTTPS and publicly reachable
* Make sure private keys live in a secrets manager, not in code or config files
* Set up alerting on webhook failures and on transfers with no settlement after \~45 minutes (the full retry window)
