
import FunnelStepFields from "../../../src/reference/subbly-sdk/types/FunnelStep.mdx";
import SubblyCartFields from "../../../src/reference/subbly-sdk/types/SubblyCart.mdx";
import CheckoutPurchaseResponseFields from "../../../src/reference/subbly-sdk/types/CheckoutPurchaseResponse.mdx";

<SdkPage group="funnels" />

A funnel has three phases: **pre-purchase**, **mid-purchase** and
**post-purchase**. In each phase you fetch the next step, then accept it or skip
it, and fetch again until `step` is `null`. For the full flow, with one example
per phase, see [Run a funnel](/developer-resources/subbly-js-sdk/run-a-funnel).

<Method id="pre-purchase-fetch" signature="subbly.funnels.prePurchaseFetch(cartId, params?)">

Gets the next pre-purchase offer for the cart. `step` comes back `null` once the
customer has seen every offer.

<Params>
  <Param name="cartId" required type="string">
    ID of the cart, usually `subbly.cart.id`.
  </Param>

  <Param name="params" optional type="FunnelsResourceParams">
    Extra data to include.
    <Properties label="params">
      <Param name="expand" optional type="string[]">
        Relations to include. `parent` loads the parent product of each offered
        variant or plan.
      </Param>
    </Properties>
  </Param>
</Params>

<Returns type="Promise<FunnelFetchResponse>">
The next offer, or an empty one.
<Properties>
  <Param name="step" type="FunnelStep | null">
    The offer to show. `null` means there is nothing left to offer.
    <Properties label="step" collapsed>
      <FunnelStepFields />
    </Properties>
  </Param>
</Properties>
</Returns>

<Example>

```js title="Show the next offer"
const { step } = await subbly.funnels.prePurchaseFetch(subbly.cart.id, {
  expand: ['parent']
})

if (step) render(step)
```

</Example>

</Method>

<Method id="pre-purchase-accept" signature="subbly.funnels.prePurchaseAccept(cartId, funnelStepId, payload, params?)">

Accepts a pre-purchase offer and adds its products to the cart.

The cart it returns is a plain object, not the `subbly.cart` model, so
`subbly.cart` still holds the old contents. Reload it with `subbly.cart.load()`.

<Params>
  <Param name="cartId" required type="string">
    ID of the cart.
  </Param>

  <Param name="funnelStepId" required type="number">
    ID of the step the customer accepted, from `step.id`.
  </Param>

  <Param name="payload" required type="FunnelAcceptPayload">
    The products the customer took.
    <Properties label="payload">
      <Param name="offers" required type="FunnelOfferPayload[]">
        One entry per product taken.
        <Properties label="offer">
          <Param name="productId" required type="number">
            The variant or plan the customer picked, from
            `step.offer.products[].id`.
          </Param>

          <Param name="options" optional type="SubscriptionSurveyOption[]">
            Survey answers, when the offered plan has a survey. Each entry is
            `{ questionId, answers }`. An answer is `{ content }` for a text or
            email question, `{ id }` for a select, multiple, offer or plan
            question, and `{ id, quantity }` for a quantity question.
          </Param>
        </Properties>
      </Param>
    </Properties>
  </Param>

  <Param name="params" optional type="CartResourceParams">
    Extra data to include on the cart that comes back.
    <Properties label="params">
      <Param name="expand" optional type="string[]">
        Cart relations to include, such as `items.product` and
        `items.product.parent`.
      </Param>
    </Properties>
  </Param>
</Params>

<Returns type="Promise<SubblyCart>">
The cart after the offer went in.
<Properties label="cart" collapsed>
  <SubblyCartFields />
</Properties>
</Returns>

<Example>

```js title="Accept an offer"
await subbly.funnels.prePurchaseAccept(subbly.cart.id, step.id, {
  offers: [{ productId: step.offer.products[0].id }]
})

await subbly.cart.load()
```

</Example>

</Method>

<Method id="pre-purchase-reject" signature="subbly.funnels.prePurchaseReject(cartId, funnelStepId)">

Skips a pre-purchase offer. Subbly records the refusal, so the next fetch moves
on to the following step.

<Params>
  <Param name="cartId" required type="string">
    ID of the cart.
  </Param>

  <Param name="funnelStepId" required type="number">
    ID of the step the customer skipped.
  </Param>
</Params>

<Returns type="Promise<void>">
Nothing. The response body is empty.
</Returns>

<Example>

```js title="Skip an offer"
await subbly.funnels.prePurchaseReject(subbly.cart.id, step.id)

const { step: next } = await subbly.funnels.prePurchaseFetch(subbly.cart.id)
```

</Example>

</Method>

<Method id="pre-purchase-refresh" signature="subbly.funnels.prePurchaseRefresh(cartId)">

Clears which pre-purchase offers the customer accepted or skipped, so the
sequence starts from the first step again. Only the pre-purchase phase has this.

<Params>
  <Param name="cartId" required type="string">
    ID of the cart.
  </Param>
</Params>

<Returns type="Promise<unknown>">
Nothing you need to read. Treat the response as empty.
</Returns>

<Example>

```js title="Start the offers again"
await subbly.funnels.prePurchaseRefresh(subbly.cart.id)
```

</Example>

</Method>

<Method id="mid-purchase-fetch" signature="subbly.funnels.midPurchaseFetch(cartId, params?)">

Gets the next mid-purchase offer for the cart. Show these while the customer
works through checkout.

<Params>
  <Param name="cartId" required type="string">
    ID of the cart.
  </Param>

  <Param name="params" optional type="FunnelsResourceParams">
    Extra data to include.
    <Properties label="params">
      <Param name="expand" optional type="string[]">
        Relations to include. `parent` loads the parent product of each offered
        variant or plan.
      </Param>
    </Properties>
  </Param>
</Params>

<Returns type="Promise<FunnelFetchResponse>">
The next offer, or an empty one.
<Properties>
  <Param name="step" type="FunnelStep | null">
    The offer to show. `null` means there is nothing left to offer.
    <Properties label="step" collapsed>
      <FunnelStepFields />
    </Properties>
  </Param>
</Properties>
</Returns>

<Example>

```js title="Show the next offer"
const { step } = await subbly.funnels.midPurchaseFetch(subbly.cart.id)
```

</Example>

</Method>

<Method id="mid-purchase-accept" signature="subbly.funnels.midPurchaseAccept(cartId, funnelStepId, payload, params?)">

Accepts a mid-purchase offer and adds its products to the cart. As with the
pre-purchase accept, reload `subbly.cart` afterwards.

<Params>
  <Param name="cartId" required type="string">
    ID of the cart.
  </Param>

  <Param name="funnelStepId" required type="number">
    ID of the step the customer accepted.
  </Param>

  <Param name="payload" required type="FunnelAcceptPayload">
    The products the customer took.
    <Properties label="payload">
      <Param name="offers" required type="FunnelOfferPayload[]">
        One entry per product taken.
        <Properties label="offer">
          <Param name="productId" required type="number">
            The variant or plan the customer picked.
          </Param>

          <Param name="options" optional type="SubscriptionSurveyOption[]">
            Survey answers, when the offered plan has a survey. Each entry is
            `{ questionId, answers }`.
          </Param>
        </Properties>
      </Param>
    </Properties>
  </Param>

  <Param name="params" optional type="CartResourceParams">
    Extra data to include on the cart that comes back.
    <Properties label="params">
      <Param name="expand" optional type="string[]">
        Cart relations to include.
      </Param>
    </Properties>
  </Param>
</Params>

<Returns type="Promise<SubblyCart>">
The cart after the offer went in.
<Properties label="cart" collapsed>
  <SubblyCartFields />
</Properties>
</Returns>

<Example>

```js title="Accept an offer"
await subbly.funnels.midPurchaseAccept(subbly.cart.id, step.id, {
  offers: [{ productId: step.offer.products[0].id }]
})

await subbly.cart.load()
```

</Example>

</Method>

<Method id="mid-purchase-reject" signature="subbly.funnels.midPurchaseReject(cartId, funnelStepId)">

Skips a mid-purchase offer so the next fetch moves on.

<Params>
  <Param name="cartId" required type="string">
    ID of the cart.
  </Param>

  <Param name="funnelStepId" required type="number">
    ID of the step the customer skipped.
  </Param>
</Params>

<Returns type="Promise<void>">
Nothing. The response body is empty.
</Returns>

<Example>

```js title="Skip an offer"
await subbly.funnels.midPurchaseReject(subbly.cart.id, step.id)
```

</Example>

</Method>

<Method id="post-purchase-fetch" signature="subbly.funnels.postPurchaseFetch(cartId, params?)">

Gets the next post-purchase offer for the cart. These run only after
`subbly.checkout.purchase` succeeds, on the thank-you page.

<Params>
  <Param name="cartId" required type="string">
    ID of the cart that was paid.
  </Param>

  <Param name="params" optional type="FunnelsResourceParams">
    Extra data to include.
    <Properties label="params">
      <Param name="expand" optional type="string[]">
        Relations to include. `parent` loads the parent product of each offered
        variant or plan.
      </Param>
    </Properties>
  </Param>
</Params>

<Returns type="Promise<FunnelFetchResponse>">
The next offer, or an empty one.
<Properties>
  <Param name="step" type="FunnelStep | null">
    The offer to show. `null` means there is nothing left to offer.
    <Properties label="step" collapsed>
      <FunnelStepFields />
    </Properties>
  </Param>
</Properties>
</Returns>

<Example>

```js title="Show the next offer after checkout"
await subbly.checkout.purchase(subbly.cart.id)

const { step } = await subbly.funnels.postPurchaseFetch(subbly.cart.id)
```

</Example>

</Method>

<Method id="post-purchase-accept" signature="subbly.funnels.postPurchaseAccept(cartId, funnelStepId, payload, params?)">

Accepts a post-purchase offer and charges it as a new purchase on the card the
customer already used. Unlike the other two accepts, this one returns the
invoice, subscription and orders it created. Needs an authenticated customer.

<Params>
  <Param name="cartId" required type="string">
    ID of the cart that was paid.
  </Param>

  <Param name="funnelStepId" required type="number">
    ID of the step the customer accepted.
  </Param>

  <Param name="payload" required type="FunnelPostAcceptPayload">
    The products taken, and anything the purchase still needs.
    <Properties label="payload">
      <Param name="offers" required type="FunnelOfferPayload[]">
        One entry per product taken.
        <Properties label="offer">
          <Param name="productId" required type="number">
            The variant or plan the customer picked.
          </Param>

          <Param name="options" optional type="SubscriptionSurveyOption[]">
            Survey answers, when the offered plan has a survey.
          </Param>
        </Properties>
      </Param>

      <Param name="shippingAddressId" optional type="number | null">
        Address to ship to. Send it when the checkout did not set one.
      </Param>

      <Param name="shippingMethodId" optional type="number | null">
        Delivery option to use. Send it when the checkout did not set one.
      </Param>

      <Param name="paymentIntentId" optional type="string | null">
        The payment intent you confirmed, when you retry after 3-D Secure.
      </Param>
    </Properties>
  </Param>

  <Param name="params" optional type="CartResourceParams">
    Extra data to include in the response.
    <Properties label="params">
      <Param name="expand" optional type="string[]">
        Relations to include, such as `subscription.product` and
        `order.metadata`.
      </Param>
    </Properties>
  </Param>
</Params>

<Returns type="Promise<FunnelPostAcceptResponse>">
What the extra purchase created.
<Properties label="purchase" collapsed>
  <CheckoutPurchaseResponseFields />
</Properties>
</Returns>

<Example>

```js title="Accept a post-purchase offer"
const { invoice, orders } = await subbly.funnels.postPurchaseAccept(
  subbly.cart.id,
  step.id,
  { offers: [{ productId: step.offer.products[0].id }] }
)
```

</Example>

</Method>

<Method id="post-purchase-reject" signature="subbly.funnels.postPurchaseReject(cartId, funnelStepId)">

Skips a post-purchase offer so the next fetch moves on.

<Params>
  <Param name="cartId" required type="string">
    ID of the cart that was paid.
  </Param>

  <Param name="funnelStepId" required type="number">
    ID of the step the customer skipped.
  </Param>
</Params>

<Returns type="Promise<void>">
Nothing. The response body is empty.
</Returns>

<Example>

```js title="Skip an offer"
await subbly.funnels.postPurchaseReject(subbly.cart.id, step.id)
```

</Example>

</Method>
