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

# Collect payment

> Where payment fits in the journey, which methods apply, and how the Stripe hand-off works

Payment is not a step at the end of the journey. On most products it sits **between confirmation and
signature**, because the policyholder signs a direct debit mandate at the same moment they sign the
contract — and on some products cover does not begin until the first premium has been collected.

Getting the ordering wrong is the most common way an otherwise correct integration stalls: the
signature request is rejected, and the error names a payment method rather than the signature.

Korint uses **Stripe** as its payment processor. We hold the Stripe account; you never call Stripe's
API server-side. Your only Stripe interaction is confirming a setup in the customer's browser with a
client secret we give you.

## What the product decides

Read these from `GET /config/{productId}` before writing any payment code:

<ResponseField name="payment.allowedPaymentModes" type="array">
  `DIRECT_DEBIT` — Korint collects from a stored payment method. `BANK_TRANSFER` — the customer
  transfers to an account you obtain from `GET /customers/{customerId}/bank-transfer-account`, and no
  payment method is collected.
</ResponseField>

<ResponseField name="payment.allowedPaymentMethods" type="array">
  `sepa_debit`, `card`, or both. These are Stripe payment method types, and they determine what the
  setup can accept.
</ResponseField>

<ResponseField name="payment.paymentMethodOnEarlyPayment / paymentMethodOnRecurringPayment" type="array">
  Which method types may be charged for the first premium and for recurring premiums. A product can
  accept a card for the first payment but require a SEPA mandate for the recurring ones.
</ResponseField>

<ResponseField name="payment.paymentMethodOnBrokenMethod" type="array">
  Fallback types charged when the normal method is unusable — a dead mandate, for example. Absent
  means such invoices are left unpaid.
</ResponseField>

<ResponseField name="signature.requirePaymentMethodForNewBusiness" type="boolean">
  When true, a new-business contract cannot be sent for signature until a payment method exists for
  every direct-debit payer.
</ResponseField>

<ResponseField name="requireFirstPaymentToActivate" type="boolean">
  A **top-level** property of the product config, not part of `payment`. When true, a signed contract
  only comes into force once the first premium has been collected, in addition to reaching its start
  date. Optional, and absent on all of the demo products — treat a missing value as false.
</ResponseField>

<ResponseField name="payment.paymentMethodIsOnBrokerageFirm" type="boolean">
  When true, the payment method belongs to the distributing brokerage firm rather than the customer,
  and you collect it on the firm's endpoints instead.
</ResponseField>

## Where payment sits in the journey

```mermaid theme={null}
sequenceDiagram
    autonumber
    participant You as Your system
    participant K as Korint API
    participant B as Customer's browser
    participant S as Stripe

    You->>K: POST /policies/{policyId}/confirm?branchId=...

    opt Payer is not on direct debit yet
        You->>K: PUT /customers/{customerId}/payment-mode
        Note over K: { "paymentMode": "DIRECT_DEBIT" }
    end

    You->>K: GET /customers/{customerId}/payment-methods/secret
    K->>S: create a SetupIntent on our account
    K-->>You: { "clientSecret": "seti_..._secret_..." }

    You->>B: render Stripe Elements with the client secret
    B->>S: confirm the setup (IBAN or card)
    S-->>B: setup succeeded
    S->>K: webhook — payment method stored

    opt SEPA mandate must be signed
        You->>K: POST /customers/{id}/payment-methods/{pmId}/signature
        Note over K: method sits in WAITING_MANDATE_SIGNATURE
    end

    You->>K: GET /customers/{customerId}/payment-methods
    K-->>You: status ACTIVE or WAITING_MANDATE_SIGNATURE

    You->>K: POST /policies/{policyId}/signature?branchId=...
    Note over K: rejected with NO_VALID_PAYMENT_METHODS<br/>if no usable method exists

    opt requireFirstPaymentToActivate
        Note over K: contract stays signed-but-not-in-force<br/>until the first premium is collected
    end
```

## Collecting a payment method

<Steps>
  <Step title="Put the payer on direct debit">
    A payment method can only be set up for a customer whose payment mode is `DIRECT_DEBIT`.

    A new customer already has one: the mode defaults to the **first entry of the product's
    `allowedPaymentModes`**. So on a direct-debit-first product this call is a no-op and you can skip it — but
    check the config rather than assuming, because on a product that lists `BANK_TRANSFER` first the default
    is bank transfer and this call is required.

    ```bash theme={null}
    curl -X PUT 'https://api.sandbox.korint.io/customers/<customerId>/payment-mode' \
      --header 'Authorization: Bearer <access_token>' \
      --header 'tenant: <tenant>' \
      --header 'Content-Type: application/json' \
      --data '{ "paymentMode": "DIRECT_DEBIT" }'
    ```

    Asking for a setup secret on a product whose `allowedPaymentModes` excludes `DIRECT_DEBIT` fails with
    `CONFLICTING_CONFIG` naming `allowedPaymentModes`.
  </Step>

  <Step title="Get a client secret">
    ```bash theme={null}
    curl 'https://api.sandbox.korint.io/customers/<customerId>/payment-methods/secret' \
      --header 'Authorization: Bearer <access_token>' \
      --header 'tenant: <tenant>'
    ```

    ```json Response theme={null}
    { "clientSecret": "seti_<id>_secret_<secret>" }
    ```

    This is a Stripe **SetupIntent** client secret, created on our Stripe account and restricted to the
    product's `allowedPaymentMethods`. It is safe to pass to the browser; it authorises setting up one
    payment method for one customer and nothing else.

    `allowedPaymentMethods` can be wider than what the contract can actually charge: the SetupIntent accepts
    everything in that list, while recurring premiums are limited to `paymentMethodOnRecurringPayment`. Render
    the intersection of the two — plus `paymentMethodOnEarlyPayment` when the first payment is an early one —
    rather than whatever the SetupIntent permits, or Stripe's Payment Element will offer a card tab that stores
    a method the contract can never charge.

    The response does not include a publishable key, and no endpoint serves one. Stripe.js must be initialised
    with the publishable key of **our** Stripe account for your tenant and environment — ask Korint for it.

    <Warning>
      **Quote the policy before asking for a secret.** The customer's counterpart on the payment side is
      created when the policy is quoted, not when the customer is created. Ask earlier in the journey — after
      creating the customer, after setting the payment mode, even after `calculate-quote` — and you get:

      ```
      404 PAYMENT_CUSTOMER_NOT_FOUND
      ```

      `POST /policies/{policyId}/quote` is what creates it; `calculate-quote` does not, because it persists
      nothing. Confirmation creates it too, for contracts that reach that point another way.
    </Warning>
  </Step>

  <Step title="Confirm the setup in the browser">
    Use Stripe.js with the client secret. For a SEPA direct debit the customer enters an IBAN; for a card,
    card details. Follow Stripe's own guides — we deliberately do not restate them here:

    <CardGroup cols={2}>
      <Card title="Stripe SetupIntents" icon="link" href="https://docs.stripe.com/payments/setup-intents">
        The setup flow the client secret belongs to.
      </Card>

      <Card title="Stripe Elements" icon="link" href="https://docs.stripe.com/payments/elements">
        Collecting IBAN or card details in the browser.
      </Card>
    </CardGroup>

    We learn the result from Stripe directly, by webhook — you do not report it back to us. The payment
    method appears on `GET /customers/{customerId}/payment-methods` shortly after the setup succeeds,
    which makes this an asynchronous step: see
    [Asynchronous operations](/get-started/asynchronous-operations).
  </Step>

  <Step title="Have the mandate signed, for SEPA">
    A SEPA direct debit needs a signed mandate authorising the collections. When the product configures
    one, request it for the stored method:

    ```bash theme={null}
    curl -X POST 'https://api.sandbox.korint.io/customers/<customerId>/payment-methods/<paymentMethodId>/signature' \
      --header 'Authorization: Bearer <access_token>' \
      --header 'tenant: <tenant>'
    ```

    Payment methods carry one of four statuses:

    * `ACTIVE` — usable; a mandate, if required, has been signed.
    * `WAITING_MANDATE_SIGNATURE` — stored, mandate not yet signed. **Accepted for new business**, which is
      what lets the customer sign the contract and the mandate in one session.
    * `INVALID_MANDATE` — no longer valid; collections will fail.
    * `EXPIRED` — the underlying method has expired, typically a lapsed card.

    Set the method the customer should be charged on with
    `PUT /customers/{customerId}/payment-methods/{paymentMethodId}/preferred` when more than one exists.
  </Step>
</Steps>

## When the first premium is taken

`invoicingConfig.firstPayment` decides this, and it is a value you set on the policy:

* `NONE` — no distinct first payment; the contract is billed on its normal schedule.
* `FULL_AT_SIGNATURE` / `EARLY_PAYMENT_AT_SIGNATURE` — charged when the contract is signed, either the
  full first premium or an early-payment portion.
* `FULL_AT_START_DATE` / `EARLY_PAYMENT_AT_START_DATE` — charged when cover begins.

If the product also sets `requireFirstPaymentToActivate`, cover does not begin until that payment has
been collected. A contract that is signed, with a start date in the past, and still not in force is
usually waiting on exactly this — not on a failure. `isAwaitingFirstPayment` on the policy tells you so;
read the invoices from `GET /customers/{customerId}/invoices`.

<Tip>
  `POST /customers/{customerId}/retry-charge` re-attempts a failed collection, and
  `GET /customers/{customerId}/balance` shows what is outstanding. For what happens when payments keep
  failing, see [Manage unpaid invoices](/concepts/payments/manage-unpaid-invoices).
</Tip>

<Warning>
  **Source of `NO_VALID_PAYMENT_METHODS` on a signature request.** `POST /policies/{policyId}/signature`
  is refused when the branch is new business, the payer is on `DIRECT_DEBIT`, the product sets
  `signature.requirePaymentMethodForNewBusiness`, and no payment method for that payer is `ACTIVE` or
  `WAITING_MANDATE_SIGNATURE`.

  An early-payment `firstPayment` adds a second condition that is easy to miss: the payer also needs a
  method whose type appears in `payment.paymentMethodOnEarlyPayment`, so a stored SEPA mandate does not
  satisfy a product that takes the first payment by card.

  The ordering to implement is confirm → payment mode → client secret → browser setup → mandate request →
  signature.
</Warning>
