# Going live

You've built and tested your integration in staging. Before processing real payments, work
through every section of this checklist. Each item maps to a specific system behavior;
none of the items here are aspirational.

Just signed up? Start with [Onboarding & KYB](https://api.fynex.ai/payments-api/v2/docs#tag/onboarding) before working through this checklist.

## 1. Token management

Get your production bearer token yourself from the **Integration** page of the production
dashboard (`https://dashboard.fynex.ai`) — log in, select your seller account, reveal the
token, and copy it (see [Authentication](https://api.fynex.ai/payments-api/v2/docs#tag/authentication)). Production and staging tokens
are independent; the production token becomes available once your KYB approval is complete
(see [section 7](#7-kyb-and-operational-mode)). If you don't have a production dashboard login
yet, ask your Fynex representative to set you up.

Once you have a production token:

- [ ] Store it in a secrets manager (HashiCorp Vault, AWS Secrets Manager, GCP Secret
  Manager, or equivalent). Never commit it to source control or write it to application
  logs.
- [ ] Load it into your application via an environment variable (`FYNEX_TOKEN`). Keep
  staging and production tokens in separate secret paths/namespaces.
- [ ] Document a token rotation procedure. Rotation is atomic — you replace the token in
  one step with no grace period. Plan for a brief maintenance window or use a blue/green
  secret-swap pattern so in-flight requests finish before the old token is retired.

#### curl (env-var loading)

```bash
# Verify the token resolves before deploying
curl -sS "$FYNEX_API/payments-api/v1/payment-methods" \
  -H "Authorization: Bearer $FYNEX_TOKEN" | jq .
```

#### JavaScript

```js
// Load token from environment — never hard-code
const token = process.env.FYNEX_TOKEN;
if (!token) throw new Error('FYNEX_TOKEN is not set');
```

#### Python

```python
import os

token = os.environ.get("FYNEX_TOKEN")
if not token:
    raise RuntimeError("FYNEX_TOKEN is not set")
```

Token rotation is available via the GraphQL mutation
`regenerateSellerAccountToken(id: Int!)` on `/dashboard/graphql`. The mutation returns a
new `SellerAccount` with the updated `authorizationToken`. Update your secret store
immediately after calling it — the old token stops working at the same moment.

## 2. Endpoint switch-over

Update `FYNEX_API` (and any dashboard URLs) across your codebase:

| | Staging | Production |
|---|---------|------------|
| API base | `https://staging-api.fynex.ai/payments-api/v1` | `https://api.fynex.ai/payments-api/v1` |
| Customer dashboard | `https://staging-dashboard.fynex.ai` | `https://dashboard.fynex.ai` |

Drive these from a single environment variable so you can switch environments without
changing application code:

```bash
# staging
FYNEX_API=https://staging-api.fynex.ai

# production
FYNEX_API=https://api.fynex.ai
```

- [ ] Confirm `FYNEX_API` is set to the production base URL in your production
  environment.
- [ ] Confirm no staging URL is hard-coded anywhere in your payment flow code.
- [ ] Smoke-test `GET $FYNEX_API/payments-api/v1/payment-methods` with the production
  token before routing real traffic.

## 3. Status verification (polling / SSE)

Fynex delivers a `PaymentCompleted` webhook to the webhook URL(s) configured on your seller
account (your receiver must return HTTP 200). Polling or SSE remain available — and are
recommended as a backstop — to verify the final payment state before fulfilling orders or
releasing goods. (Payouts do not yet emit webhooks; poll for payout status.)

- [ ] Your fulfilment logic reads payment status from the API — it does not rely solely
  on a redirect URL or a query parameter that the customer could manipulate.
- [ ] Your polling loop has a maximum number of attempts and a fallback (e.g., mark the
  order as "pending review" after 10 minutes of inconclusive polling).
- [ ] SSE (`/checkout/{session_id}/events`) is only available while the customer's
  browser is on the hosted checkout page. For server-side verification, use the GraphQL
  `genericPayment(id)` query.

See the [Polling & SSE](https://api.fynex.ai/payments-api/v2/docs#tag/polling-sse) guide for code samples and status
transition reference.

## 4. Idempotency persistence

- [ ] Every `POST` request (`/initialize-payment`, `/finalize-payment`, `/checkout`,
  `/payments/{id}/capture`, `/payments/{id}/refund`, `/payouts`) sends a unique
  `Idempotency-Key` UUID.
- [ ] Idempotency keys are **persisted with the order** in your database before the
  request is sent. A pod restart, crash, or retry must reuse the original key — not
  generate a new one.
- [ ] Keys are generated from a cryptographically secure source (e.g., `crypto.randomUUID()`
  in Node, `uuid.uuid4()` in Python). Do not use `Math.random()` or sequential IDs.
- [ ] You treat `200 OK` from a repeated create as a successful idempotency replay. A
  `409 Conflict` means the key was reused with different financial fields and must be
  handled as an error, not as success.

See [Idempotency & retries](https://api.fynex.ai/payments-api/v2/docs#tag/idempotency) for the full replay contract.

## 5. Logging and monitoring

Wire alerts on these signals:

- [ ] HTTP error rate on Fynex API calls above 1% sustained over 5 minutes.
- [ ] `502 Bad Gateway` rate above 0.5% (signals an upstream provider issue).
- [ ] Payment status remaining in a non-terminal state (e.g., `new`, `routed`,
  `provider_pending`) for more than 15 minutes.
- [ ] Capture-to-refund ratio outside your expected range.
- [ ] Decline rate rising more than 5% week-over-week.

> [!CAUTION]
> Do not log the raw card number (PAN), CVV, or full track data at any point in your
> pipeline. Audit your log pipeline — structured logging frameworks can inadvertently
> serialize entire request bodies.

Log the Fynex `paymentId` (the numeric ID on the `GenericPayment` object) alongside
your internal order ID on every payment event. This is what Fynex support will ask for
when you raise an issue.

> [!NOTE]
> There are no `X-Fynex-Request-Id` or `X-Fynex-Trace-Id` response headers in the
> current API. Use the `paymentId` from the response body as your primary correlation key.

## 6. Customer experience

- [ ] **Decline messaging** — use the human-readable `failureDescription` field from
  the payment response to show the customer why a payment failed. Do not display the
  numeric `failureCode` directly; treat it as an opaque internal identifier.
- [ ] **Retry with a different card** — after a decline, your UI should offer a clear
  path to re-enter card details. Generate a fresh `Idempotency-Key` for the retry
  attempt.
- [ ] **3DS handling** — if your server-to-server integration receives
  `requiresAction: true` with a `redirectUrl`, redirect the customer immediately and
  preserve the original `Idempotency-Key` for the finalize call. Do not generate a new
  key after the 3DS redirect. See [Server-to-server](https://api.fynex.ai/payments-api/v2/docs#tag/server-to-server) for the
  full flow.
- [ ] **Error state UI** — distinguish between "payment failed" (terminal) and "payment
  status unknown" (polling timed out). Show different messages and offer appropriate
  next steps.

See [Errors](https://api.fynex.ai/payments-api/v2/docs#tag/errors) for the full error shape and status code reference.

## 7. KYB and operational mode

Each seller account has an `OperationalMode` field that is either `Demo` or `Live`
(the GraphQL `SellerAccountOperationalMode` enum is case-sensitive — capitalized).
Payments processed in `Demo` mode do not move real funds.

1. **Complete KYB** — your Fynex representative will guide you through the KYB
   (Know Your Business) submission via the KYB provider's flow. You can check KYB status via
   the GraphQL `kybStatus` query.

2. **Wait for approval** — Fynex staff review and approve the KYB submission.

3. **Flip operational mode** — after KYB approval, contact your Fynex representative
   to switch your seller account from `Demo` to `Live`. This flip is staff-mediated
   today; there is no self-service control for it in the seller dashboard.

4. **Confirm the mode** — query your seller account via GraphQL to verify
   `operationalMode` is `Live` before routing real traffic.

> [!WARNING]
> Do not route real customer payments to an account still in `Demo` mode. The API will
> accept the request but no real funds will be processed or settled.

## 8. PCI scope

| Integration type | PCI scope |
|-----------------|-----------|
| Hosted checkout | SAQ A — card data never touches your servers |
| Server-to-server | SAQ D — your servers handle card numbers; full PCI assessment required |

- [ ] PCI scope confirmed with your compliance team.
- [ ] Customer-facing privacy policy mentions Fynex as a payment processor.
- [ ] Logs never contain PAN, CVV, or full track data (see [section 5](#5-logging-and-monitoring)).

## 9. Sandbox test cards

> [!CAUTION]
> Test cards work **only on a Demo account** — that is decided by the account's operational
> mode, not by which host you call. Do not ship code that hard-codes them; on a Live account
> they are declined, or worse, charged if the number happens to belong to a real card.

Fynex's sandbox is backed by an upstream card processor's test environment, where the PAN
selects the 3DS authentication outcome. `4000 0000 0000 2701` (Visa) and
`5200 0000 0000 2235` (Mastercard) authenticate frictionlessly and settle — use those for a
success. On any flow that runs 3DS (the hosted checkout always does), a PAN that does not
authenticate cannot be settled. See the [Test cards & sandbox](https://api.fynex.ai/payments-api/v2/docs#tag/test-cards) guide for the
full set of sandbox card numbers and decline codes.

Use any future expiry date and any 3-digit CVV in the sandbox.

## 10. Cutover plan

Run a cautious rollout rather than flipping all traffic at once:

1. **Internal soft-launch** — route only your own team's test orders through production.
   Verify a real card payment, a capture, and a refund end-to-end.

2. **5% canary** — route 5% of live traffic to Fynex for 24 hours. Monitor decline rate,
   error rate, and latency p99.

3. **Ramp to 50%** — after 48 hours of clean canary metrics, increase to 50%.

4. **Full cut-over** — after a further 72 hours of clean data, move to 100%.

Keep a feature flag that can re-route payments back to your previous processor. Leave
it in place for at least two weeks post-cutover. If anything goes wrong during ramp-up,
the rollback is a single config change.

## 11. Post-launch review

Schedule a review 7 days after full cut-over:

- [ ] Decline rate — compare to your baseline from staging and industry benchmarks.
- [ ] Dispute / chargeback rate — should be near zero in the first week; investigate any
  spike immediately.
- [ ] Refund rate — track against your expected return rate.
- [ ] Support ticket volume — identify any friction in the payment flow from customer
  complaints.
- [ ] Reconciliation — verify settlement amounts match your expected revenue.

> [!NOTE]
> If anything looks off, reach out at **support@fynex.ai** with your seller account ID
> and the relevant `paymentId` values and we'll investigate.

## See also

- **[Authentication](https://api.fynex.ai/payments-api/v2/docs#tag/authentication)** — How bearer tokens work, how to rotate them, and auth failure modes.
- **[Polling & SSE](https://api.fynex.ai/payments-api/v2/docs#tag/polling-sse)** — Verify payment outcomes without webhooks.
- **[Errors](https://api.fynex.ai/payments-api/v2/docs#tag/errors)** — Status codes, error shapes, and how to handle declines.
- **[Idempotency & retries](https://api.fynex.ai/payments-api/v2/docs#tag/idempotency)** — Safe retry patterns and key persistence requirements.
