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

# Go Live

> What to verify, what to change, and how to debug when switching to production.

Working in the sandbox is not the same as being ready to launch. This page lists what to clear first.

## Complete account verification

Taking real payments and receiving payouts requires account verification.

Submit your details under **Settings**, where Clink reviews your payment entity and settlement eligibility. Status is one of not started, under review, rejected, or approved.

For merchants going through onboarding, the rest of Settings unlocks once business verification (KYB) or personal verification (KYC) is approved. Creating a product or completing a first payment is not required.

<Note>
  Some production accounts see only the first step, **Complete account verification**, with nothing after it. For those accounts that step unlocks payouts.
</Note>

Verification and integration work run in parallel. While production review is pending you can keep building in the sandbox — the sandbox has its own verification step, which is approved automatically and does not depend on production approval.

## Verify in four layers

Work through these in order. Do not move up a layer until the one below passes.

<Steps>
  <Step title="Local logic">
    No real payments. Verify amount calculation, product snapshots, idempotency keys, state transitions, and fulfillment deduplication. Unit tests cover this layer.
  </Step>

  <Step title="Simulated webhooks">
    Send signed requests to your own webhook endpoint and verify: the raw body is read correctly, the HMAC matches, repeated events do not fulfill twice, out-of-order events do not overwrite newer state, and bad signatures are rejected.
  </Step>

  <Step title="Real sandbox Session">
    Have your backend create a real sandbox Checkout Session and confirm the frontend redirects or mounts correctly.
  </Step>

  <Step title="Real sandbox payment">
    Pay with a test card end to end. Confirm your local order becomes paid **and** that shipping, top-up, or entitlement actually ran.
  </Step>
</Steps>

<Warning>
  Layer four passes when the business outcome is complete, not when the webhook returned 200. Check the fulfillment status in your own database rather than reading logs.
</Warning>

## Scenario checklist

| Scenario              | Confirm                                                                                                                                   |
| --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| Product mode          | Registered products use the right `productId`/`priceId`; inline `priceDataList` amounts and currency add up to the total                  |
| Frontend              | Session creation, redirect, or mounting all work; the browser only receives the Publishable Key and Session fields                        |
| Payment methods       | Test every method you have enabled, including QR codes and flows with extra verification                                                  |
| Multi-currency        | Fixed multi-currency prices or automatic conversion produce the right result; the settled amount and currency reconcile against the Order |
| Success               | The order becomes paid, fulfillment runs, and runs exactly once                                                                           |
| Failure               | `failureCode` is recorded and the customer can retry                                                                                      |
| `pending`             | Shows "confirming" and never triggers an automatic second charge                                                                          |
| `requires_action`     | The customer can complete 3DS or other verification                                                                                       |
| Duplicate webhooks    | The same event delivered three times fulfills once                                                                                        |
| Out-of-order webhooks | `session.expired` arriving before `order.succeeded` still ends in a successful state                                                      |
| Success after expiry  | An expired entry point whose payment later succeeds is not treated as a failure                                                           |
| Refunds               | The same `refundMerchantOrderId` twice does not refund twice; refund status syncs                                                         |
| Subscriptions         | First payment, trial, renewal, `past_due`, customer portal, and cancellation each move entitlements correctly                             |
| Disputes              | `dispute.created`, `dispute.updated`, `dispute.won`, `dispute.lost`, `dispute.closed` route into manual handling                          |

### Also verify these if you have subscriptions

| Scenario            | Confirm                                                                                                                                    |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| First subscription  | A recurring price completes a first subscription through Hosted Checkout, and the Subscription, Invoice, and Order IDs are all persisted   |
| Free trial          | Access opens only after `subscription.trialing`, and the first charge after the trial is not started twice                                 |
| `past_due`          | It is not treated as paid, and does not trigger a second concurrent charge                                                                 |
| Cancellation        | End-of-period and immediate cancellation end access at different times, and immediate cancellation is not described as an automatic refund |
| Renewal idempotency | Renewals are idempotent on `invoiceId`; duplicate or out-of-order `invoice.paid` does not extend access twice                              |

### Also verify these if you offer discounts

| Scenario          | Confirm                                                                                                                                                    |
| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Every code path   | Visible input, hidden pre-applied, invalid code, expired code, minimum spend not met, product mismatch, and fixed-amount currency mismatch are each tested |
| Discount duration | `once`, `repeating`, and `forever` produce the expected number of periods both with and without a free trial                                               |
| Amount display    | The frontend shows only the original price, discount, and amount due returned by the backend — nothing recalculated locally                                |

<Note>
  The only sandbox test data published today is the success card `4242 4242 4242 4242`. There is no public test data for `failed`, 3DS, or `pending`.

  Those three rows therefore cannot currently be reproduced in the sandbox, so cover them with code review and unit tests instead. Read the `pending` path carefully: confirm your code does not treat it as a failure and start a second charge, which is the most common cause of double billing.
</Note>

## What to change for production

| Item                                     | From                                  | To                                         |
| ---------------------------------------- | ------------------------------------- | ------------------------------------------ |
| Dashboard                                | `https://uat-dashboard.clinkbill.com` | `https://dashboard.clinkbill.com`          |
| API                                      | `https://uat-api.clinkbill.com`       | `https://api.clinkbill.com`                |
| Secret Key                               | `sk_uat_…`                            | `sk_prod_…`                                |
| Publishable Key                          | `pk_uat_…`                            | `pk_prod_…`                                |
| Webhook URL                              | Temporary tunnel address              | Your production domain                     |
| Webhook signing key                      | The sandbox one                       | Newly generated on the production endpoint |
| `successUrl` / `cancelUrl` / `returnUrl` | Local or sandbox domains              | Production domain                          |

Everything above is environment-specific configuration. **The account itself does not change** — sandbox and production share one login, and you switch with **Enter production** in the top right of the sandbox dashboard. There is no second registration.

<Warning>
  Production keys must be initialized in the **production dashboard**, and the webhook endpoint registered again in production with a different signing key. Miss this and every production event fails signature verification — quietly. Your service keeps returning 401, customers pay, and nothing ever ships.
</Warning>

After launch, run one small real transaction end to end and confirm payment, webhook, fulfillment, and reconciliation all work before opening the traffic.

## Debugging

### The customer says they paid, but the order did not change

Work down this list:

1. Look for the transaction on the **Transactions** page — in the dashboard for the matching environment, since sandbox and production are separate
2. If it is not there, call `GET /checkout/session/{id}` with the `sessionId` and read `status` and `orderId`
3. If there is an `orderId`, call `GET /order/{id}` and check whether Order `status` is `success`
4. If the Order is `success` but your order did not update, the problem is in the webhook path — keep reading

### Webhooks never arrive

| Check                            | Notes                                                      |
| -------------------------------- | ---------------------------------------------------------- |
| Is the URL public HTTPS          | localhost, private IPs, and loopback are rejected          |
| Is the endpoint enabled          | Check **Developers > Webhooks**                            |
| Are the events subscribed        | Subscribing only to `session.*` means no `order.succeeded` |
| What does your service return    | Anything other than 2xx counts as a failure and is retried |
| Is a firewall or WAF blocking it | Check whether the requests reach your access logs at all   |

### Signature verification always fails

The three usual causes:

* The parsed body was used instead of the raw one (`express.json()` in Express)
* The signing key does not belong to the current endpoint — it was rotated, or copied from the sandbox
* The `.` between timestamp and body is missing, or a self-generated timestamp was used instead of the header value

### A customer was charged twice

Check these:

* Whether your create-order endpoint guards against double submission, and whether one cart can produce two orders
* Whether `pending` was treated as failure and triggered an automatic retry
* Whether webhooks are deduplicated by `event.id`

`merchantReferenceId` is not an idempotency key. Creating two Sessions with the same value gives you two Sessions, and both can be paid.

### Account verification was rejected

**Settings** shows the specific reason. Correct the details and resubmit. Integration work is unaffected — keep building in the sandbox.

## After launch

<CardGroup cols={2}>
  <Card title="Balance & payouts" icon="circle-dollar-sign" href="/finance/balance">
    When money becomes withdrawable and how fees are calculated.
  </Card>

  <Card title="Refunds" icon="rotate-ccw" href="/guides/resources/refund">
    The refund flow and status synchronization.
  </Card>
</CardGroup>
