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

# Stripe

> Set up Stripe Checkout for card payments on your own Stripe account in 44 countries: the secret key, the webhook URL and its two events, and the signing secret.

## Collect payments with Stripe

`STRIPE` sends the payer to a **Stripe-hosted Checkout page** to pay by card.
Apple Pay and Google Pay can appear there too, because Stripe treats them as
cards. Centipid Billing
creates the Checkout Session, sends the payer to it, and confirms the result on
a signed webhook that is re-checked against Stripe before anything is credited.

The money goes into **your own Stripe account**. Centipid Billing never holds
it, and this is not Stripe Connect: you open the account with Stripe yourself
and paste its keys here.

Offered in the country catalog for the 44 countries where Stripe opens accounts
online: Australia, Austria, Belgium, Brazil, Bulgaria, Canada, Croatia, Cyprus,
the Czech Republic, Denmark, Estonia, Finland, France, Germany, Gibraltar,
Greece, Hong Kong, Hungary, Ireland, Italy, Japan, Latvia, Liechtenstein,
Lithuania, Luxembourg, Malaysia, Malta, Mexico, the Netherlands, New Zealand,
Norway, Poland, Portugal, Romania, Singapore, Slovakia, Slovenia, Spain, Sweden,
Switzerland, Thailand, the United Arab Emirates, the United Kingdom and the
United States.

<Note>
  Stripe is not offered in Kenya, Nigeria, Ghana, South Africa or Côte d'Ivoire.
  Stripe serves those countries through Paystack, which is already in the
  catalog. India and Indonesia are invite-only at Stripe.
</Note>

## Before you start

1. Open a Stripe account owned by the ISP and finish activation, so it can take
   live payments.
2. Make sure **Cards** is turned on in **Settings → Payment methods**. Centipid
   Billing asks Stripe for card payments only. Bank debits and voucher methods
   (SEPA, Bacs, ACH, OXXO, Konbini, Boleto) are left out on purpose: the money
   arrives hours or days later, so the payer could not go online at once.
3. Plan the walled garden: the payer's browser must reach Stripe's Checkout
   page, so a Hotspot subscriber who is not yet online cannot pay unless Stripe
   is reachable before login. Centipid Billing adds Stripe's hosts to your
   routers' walled garden when Stripe is your connected gateway.

<Warning>
  Some cards, common in the UK and EU, need a 3-D Secure check on the card
  issuer's own website. That website is the bank's, so it cannot be added to the
  walled garden in advance. A Hotspot payer who is not yet online may fail that
  check and be unable to pay with that card. Subscribers paying from PPPoE or
  from the customer portal are already online and are not affected.
</Warning>

## Configure the gateway

<Steps>
  <Step title="Open Settings → Payments → Stripe">
    Select Stripe from the payment marketplace.
  </Step>

  <Step title="Enter the secret key">
    In Stripe, open **Developers → API keys** and copy the **Secret key**. It
    starts with `sk_live_` on a live account and `sk_test_` in test mode. A
    restricted key (`rk_live_…`) also works if it has write access to Checkout
    Sessions. The publishable key (`pk_…`) is not needed and is refused.
  </Step>

  <Step title="Add the webhook in Stripe">
    Copy the **Webhook URL** shown under Callback URLs on the Stripe settings
    page. In Stripe, open **Developers → Webhooks → Add endpoint**, paste the
    URL, and select exactly these two events:

    * `checkout.session.completed`
    * `checkout.session.async_payment_succeeded`
  </Step>

  <Step title="Enter the webhook signing secret">
    On the endpoint you just created, reveal the **Signing secret**. It starts
    with `whsec_`. Paste it into **Webhook signing secret**.
  </Step>

  <Step title="Run a controlled test payment">
    Pay a small amount from the captive portal on a device that is not logged
    in, and confirm the payment reaches the ledger and the subscriber connects.
  </Step>
</Steps>

## Credentials

| Field | Required | Notes |
| - | - | - |
| Secret key | Yes | Secret. `sk_live_…`, `sk_test_…`, or a restricted `rk_…` key. |
| Webhook signing secret | Yes | Secret. `whsec_…`, from the webhook endpoint in Stripe. |

Both fields gate the switch. Each webhook endpoint has its own signing secret,
so if you delete and re-create the endpoint, paste the new secret too.

## Callback URLs

| Name | Path |
| - | - |
| Webhook URL | `https://<your-host>/api/stripe/callback` |

The payer's browser returns to `https://<your-host>/api/stripe/return` after
Checkout. Centipid Billing sends that address with every payment, so there is
nothing to register for it.

<Warning>
  Stripe signs each webhook with the endpoint's **signing secret** and a
  timestamp. A wrong or missing secret means every webhook is rejected with
  HTTP 400. Payers who wait for the return page are still connected, but a
  payer who closes the tab before it loads is charged and left disconnected
  until someone reconciles by hand.
</Warning>

## What the payer must provide

An email address and card details, or an Apple Pay or Google Pay approval
where the payer's device offers one. Stripe may also ask the card's bank for a
3-D Secure check, which opens a page on the bank's own website.

The currency is your account's billing currency, defaulting to `USD`. Stripe
must support that currency for your country.

<Note>
  Amounts are sent in the currency's smallest unit: £5.50 is sent as `550`.
  Currencies without minor units, such as JPY, are sent as they are: ¥500 is
  `500`.
</Note>

### Minimum charge

Stripe refuses a payment below its minimum for the currency, so a very cheap
package cannot be paid through Stripe. Among the currencies its countries use:

| Currency | Minimum |
| - | - |
| USD | 0.50 |
| GBP | 0.30 |
| EUR | 0.50 |
| JPY | 50 |
| THB | 10 |
| MXN | 10 |

The full list, and how it works when Stripe converts to another payout
currency, is in Stripe's
[minimum and maximum charge amounts](https://docs.stripe.com/currencies#minimum-and-maximum-charge-amounts).
Price packages sold through Stripe at or above the minimum.

## How a payment completes

<Steps>
  <Step title="Create the Checkout Session">
    Centipid Billing creates a Checkout Session for the amount, tagged with our
    own reference, and writes a pending row against that reference.
  </Step>

  <Step title="Payment">
    The payer pays on Stripe's Checkout page and is sent back to the return page.
  </Step>

  <Step title="Confirmation">
    Stripe posts the signed webhook. The signature and its timestamp are
    checked, the session is then read back from the Stripe API, and only a
    session whose `payment_status` is `paid` records a payment and applies the
    package. The return page runs the same check. A repeated webhook updates the
    same payment rather than crediting it twice.
  </Step>
</Steps>

<Note>
  Card payments settle before the payer returns, so
  `checkout.session.completed` normally carries them. Subscribing to
  `checkout.session.async_payment_succeeded` as well covers a payment Stripe
  confirms later.
</Note>

Events for sessions that other software, or another Centipid Billing account,
created on the same Stripe account are acknowledged and ignored. Sharing the
Stripe account is safe.

If Stripe's API cannot be reached when a webhook arrives, Centipid Billing
answers with HTTP 503 so Stripe sends the webhook again later.

## Troubleshooting

| Symptom | What it means | What to do |
| - | - | - |
| *"Unable to process payment. Please try again later."* | The secret key is missing or Stripe refused the session. | Check the secret key, and that the account is activated for live payments. |
| *"Unable to process payment"* only on cheap packages | The package price is below Stripe's [minimum charge](#minimum-charge) for your currency. | Raise the price to at least the minimum, or sell that package another way, such as vouchers. |
| A Hotspot payer's card fails at the bank check | The card needs 3-D Secure, and the bank's page is not reachable before login. | Ask the payer to try another card, or to buy a voucher instead. |
| Test payments work, live does not | The key is still `sk_test_…`. | Replace it with the live secret key, and re-create the webhook in live mode: test-mode and live-mode webhooks have different signing secrets. |
| The Checkout page never loads on a Hotspot device | The walled garden does not allow Stripe. | Confirm Stripe is the connected gateway so the routers are synced, then see [Captive device cannot reach checkout](/billing/payment-gateways/troubleshooting). |
| Payments succeed in Stripe, ledger stays empty | Webhooks are being rejected or not sent. | In Stripe, open the endpoint's event log. A 400 means the signing secret is wrong: re-copy it. No attempts means the two events are not selected. |

For every gateway's fields in one place, see the
[credential reference](/billing/payment-gateways/credentials-reference).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.