
import SubblyCartFields from "../../../src/reference/cart-widget/types/SubblyCart.mdx";
import SubblyShopFields from "../../../src/reference/cart-widget/types/SubblyShop.mdx";
import CartUpdatePayloadFields from "../../../src/reference/cart-widget/types/CartUpdatePayload.mdx";
import ConfigureItemPayloadFields from "../../../src/reference/cart-widget/types/ConfigureItemPayload.mdx";
import CartItemAddPayloadFields from "../../../src/reference/cart-widget/types/CartItemAddPayload.mdx";
import CartUpdateItemPayloadFields from "../../../src/reference/cart-widget/types/CartResourceUpdateItemPayload.mdx";
import ShopCartSettingsFields from "../../../src/reference/cart-widget/types/ShopCartSettings.mdx";

<CartWidgetPage group="methods" />

<Method id="open" signature="subblyCart.open(view?)">

Opens the widget on the cart summary, or on the checkout. The call does
nothing when the widget is already open. It emits `CART_OPEN`.

<Params>
  <Param name="view" optional type='"summary" | "checkout" | string | boolean'>
    Which view to open. Default `"summary"`.

    - `"summary"` shows the cart summary and closes the checkout panel.
    - `"checkout"` opens the checkout. When `checkoutUrl` is configured, the
      widget sends the page to that URL with the cart ID appended and stops, so
      nothing opens. The embedded checkout view needs both the
      `checkoutWidget` and the `embedCheckout` limits on the shop; without
      them the widget sends the page to the hosted Subbly checkout instead.
    - Any other string opens the widget without changing the view.
    - `true` opens the widget with the summary shown, `false` with it hidden.
      Both leave the active customization view mounted.

    Every string value unmounts the customization view, unless `"checkout"`
    hands over to another page.
  </Param>
</Params>

<Returns type="void" />

<Example>

```js title="Open the widget"
subblyCart.open()

// Open the checkout
subblyCart.open('checkout')

// Open without the summary
subblyCart.open(false)
```

</Example>

</Method>

<Method id="close" signature="subblyCart.close()">

Closes the widget. It hides the summary, unmounts any customization view, and
clears the widget error. The call does nothing when the widget is already
closed. It emits `CART_CLOSE`.

<Returns type="void" />

<Example>

```js title="Close the widget"
subblyCart.close()
```

</Example>

</Method>

<Method id="toggle" signature="subblyCart.toggle()">

Opens the widget when it is closed, closes it when it is open. It always opens
on the summary. It emits `CART_OPEN` or `CART_CLOSE`.

<Returns type="void" />

<Example>

```js title="Toggle the widget"
subblyCart.toggle()
```

</Example>

</Method>

<Method id="load-shop" signature="subblyCart.loadShop()">

Returns the shop the widget already holds. It refreshes `subblyCart.shop` in
place, so every reference you hold sees the same object. It calls the API only
when no shop is loaded yet, which cannot happen after `initialize`, so in
practice it never re-reads the shop.

<Returns type="Promise<SubblyShop>">
The shop.
<Properties label="shop" collapsed>
  <SubblyShopFields />
</Properties>
</Returns>

<Example>

```js title="Reload the shop"
const shop = await subblyCart.loadShop()
```

</Example>

</Method>

<Method id="update-cart" signature="subblyCart.updateCart(payload)">

Applies cart-level changes — currency, coupon, addresses, shipping, gift
details — and refreshes the widget, open or closed. Calls run in order through
an internal queue, so concurrent updates do not race each other. A cart that
was already purchased is reset first, which emits `CART_RESET`. The method
throws `The cart is not initialized` when the widget holds no cart. It emits
`CART_UPDATED`.

<Params>
  <Param name="payload" required type="Partial<CartUpdatePayload>">
    The cart fields to change. Every field is optional.

    <Properties label="payload" collapsed>
      <CartUpdatePayloadFields />
    </Properties>
  </Param>
</Params>

<Returns type="Promise<SubblyCart>">
The updated cart.
<Properties label="cart" collapsed>
  <SubblyCartFields />
</Properties>
</Returns>

<Example>

```js title="Set the shipping address"
const cart = await subblyCart.updateCart({
  shippingAddress: {
    firstName: 'Ada',
    lastName: 'Lovelace',
    addressOne: '12 Bishop Street',
    addressTwo: null,
    city: 'Bristol',
    zip: 'BS2 8EE',
    countryId: 826
  }
})
```

</Example>

</Method>

<Method id="configure-item" signature="subblyCart.configureItem(payload)">

Adds a product, a bundle, or a survey, and asks the customer for what is
missing. Use it when you hold a parent product ID, a bundle ID, or a survey ID,
and `addItem` when you already hold a variant or a plan.

The widget creates a cart when there is none, and opens itself unless the shop
setting `afterItemAdded` is `close`. It then mounts the view the item needs:
pricing, variant, survey, survey flow, bundle, bundle plan, voucher,
subscription replacement, out of stock, or error. It emits `CART_UPDATED` once
the item lands in the cart.

<Params>
  <Param name="payload" required type="ConfigureItemPayload">
    What to add. Pass one of `productId`, `bundleId` or `surveyId`.

    <Properties label="payload" collapsed>
      <ConfigureItemPayloadFields />
    </Properties>
  </Param>
</Params>

<Returns type="Promise<{ finalized: boolean }>">
The promise stays pending while the customization view is open, and resolves
once the customer closes it. It never resolves with `finalized: false`, so the
useful signal is when it resolves, not what it carries.
<Properties>
  <Param name="finalized" type="boolean">
    `true`. The item is in the cart.
  </Param>
</Properties>
</Returns>

<Example>

```js title="Add a subscription product"
const { finalized } = await subblyCart.configureItem({
  productId: 555,
  quantity: 1
})
```

```js title="Start a bundle or a survey"
await subblyCart.configureItem({ bundleId: 42 })

await subblyCart.configureItem({ surveyId: 7 })
```

</Example>

</Method>

<Method id="add-item" signature="subblyCart.addItem(payload)">

Adds an item that needs no further input straight to the cart. The `productId`
must be a variant ID or a plan ID. A cart that was already purchased is reset
first, which emits `CART_RESET`. On failure the widget shows its error view
**and** rejects, so your `catch` runs as well. It emits `CART_UPDATED`.

<Params>
  <Param name="payload" required type="CartItemAddPayload">
    The item to add.

    <Properties label="payload" collapsed>
      <CartItemAddPayloadFields />
    </Properties>
  </Param>
</Params>

<Returns type="Promise<SubblyCart>">
The updated cart.
<Properties label="cart" collapsed>
  <SubblyCartFields />
</Properties>
</Returns>

<Example>

```js title="Add a variant"
const cart = await subblyCart.addItem({
  productId: 8123,
  quantity: 2
})
```

</Example>

</Method>

<Method id="update-item" signature="subblyCart.updateItem(cartItemId, payload)">

Changes a line that is already in the cart. The payload has no required field,
so TypeScript cannot tell the three shapes apart; pick the one that matches the
line. There is no `productId` and no `metadata` — both are add-time only. It
emits `CART_UPDATED`.

<Params>
  <Param name="cartItemId" required type="string">
    ID of the line, as a UUID. Read it from `subblyCart.cart.items[].id`.
  </Param>
  <Param name="payload" required type="CartResourceUpdateItemPayload">
    The fields to change.

    <Properties label="payload" collapsed>
      <CartUpdateItemPayloadFields />
    </Properties>
  </Param>
</Params>

<Returns type="Promise<SubblyCart>">
The updated cart.
<Properties label="cart" collapsed>
  <SubblyCartFields />
</Properties>
</Returns>

<Example>

```js title="Change the quantity"
const [item] = subblyCart.cart.items

const cart = await subblyCart.updateItem(item.id, { quantity: 3 })
```

</Example>

</Method>

<Method id="remove-item" signature="subblyCart.removeItem(cartItemId)">

Removes a line from the cart. It emits `CART_UPDATED`.

<Params>
  <Param name="cartItemId" required type="string">
    ID of the line, as a UUID. Read it from `subblyCart.cart.items[].id`.
  </Param>
</Params>

<Returns type="Promise<SubblyCart>">
The updated cart.
<Properties label="cart" collapsed>
  <SubblyCartFields />
</Properties>
</Returns>

<Example>

```js title="Remove an item"
const [item] = subblyCart.cart.items

const cart = await subblyCart.removeItem(item.id)
```

</Example>

</Method>

<Method id="apply-coupon" signature="subblyCart.applyCoupon(couponCode)">

Applies a coupon code to the cart. It is shorthand for
`updateCart({ couponCode })`, and it emits `CART_UPDATED`. The call rejects
when the coupon is invalid or does not fit what the cart holds, so a coupon
tied to a product fails on an empty cart. Use `setPendingCoupon` to hold such a
code until the product is in the cart.

<Params>
  <Param name="couponCode" required type="string">
    The code the customer typed.
  </Param>
</Params>

<Returns type="Promise<SubblyCart>">
The updated cart.
<Properties label="cart" collapsed>
  <SubblyCartFields />
</Properties>
</Returns>

<Example>

```js title="Apply a coupon"
const cart = await subblyCart.applyCoupon('WELCOME10')
```

</Example>

</Method>

<Method id="set-pending-coupon" signature="subblyCart.setPendingCoupon(couponCode)">

Holds a coupon code and applies it once the cart holds the right products.
Nothing happens at once: the widget applies the code the next time the summary
or the checkout renders, so a product-restricted coupon is checked against a
cart that already holds the product.

The code is dropped after one attempt, and skipped when it matches the coupon
already on the cart. Failures are silent. This is what a `?coupon=CODE` buy
link uses.

<Params>
  <Param name="couponCode" required type="string">
    The code to hold.
  </Param>
</Params>

<Returns type="void" />

<Example>

```js title="Queue a coupon, then add the product"
subblyCart.setPendingCoupon('WELCOME10')

await subblyCart.configureItem({ productId: 555 })
```

</Example>

</Method>

<Method id="remove-coupon" signature="subblyCart.removeCoupon()">

Removes the coupon from the cart. It calls `updateCart({ couponCode: null })`
and emits `CART_UPDATED`.

<Returns type="Promise<SubblyCart>">
The updated cart.
<Properties label="cart" collapsed>
  <SubblyCartFields />
</Properties>
</Returns>

<Example>

```js title="Remove the coupon"
const cart = await subblyCart.removeCoupon()
```

</Example>

</Method>

<Method id="apply-gift-card" signature="subblyCart.applyGiftCard(giftCardCode)">

Applies a gift card code to the cart. It is shorthand for
`updateCart({ giftCardCode })`, and it emits `CART_UPDATED`.

<Params>
  <Param name="giftCardCode" required type="string">
    The gift card code, as a UUID.
  </Param>
</Params>

<Returns type="Promise<SubblyCart>">
The updated cart.
<Properties label="cart" collapsed>
  <SubblyCartFields />
</Properties>
</Returns>

<Example>

```js title="Apply a gift card"
const cart = await subblyCart.applyGiftCard(
  '0b5c0f1a-8f3c-4f2a-9f0e-6a2f6a1f2b3c'
)
```

</Example>

</Method>

<Method id="remove-gift-card" signature="subblyCart.removeGiftCard()">

Removes the gift card from the cart. It calls
`updateCart({ giftCardCode: null })` and emits `CART_UPDATED`.

<Returns type="Promise<SubblyCart>">
The updated cart.
<Properties label="cart" collapsed>
  <SubblyCartFields />
</Properties>
</Returns>

<Example>

```js title="Remove the gift card"
const cart = await subblyCart.removeGiftCard()
```

</Example>

</Method>

<Method id="set-language" signature="subblyCart.setLanguage(langCode)">

Switches the language of the widget and asks the API for translated responses.
The shop must publish the language; otherwise the widget falls back to the
shop's own languages, then to `en`. It updates `subblyCart.state.languageCode`
and emits no event. An empty value throws
`Cart Widget:setLanguage Language code is required`.

Without this call the widget takes the language from `languageCode` in the
config. If that is missing it tries the `lang` query parameter, then the `lang`
attribute of `<html>`, then the browser.

<Params>
  <Param name="langCode" required type="string">
    Two-letter language code, such as `fr`. It must be one of
    `subblyCart.shop.languages[].code`.
  </Param>
</Params>

<Returns type="void" />

<Example>

```js title="Switch to French"
subblyCart.setLanguage('fr')
```

</Example>

</Method>

<Method id="set-currency" signature="subblyCart.setCurrency(currencyCode)">

Switches the currency of the cart and re-prices every line. It also re-prices
the products, bundles and surveys the SDK loads next, and updates
`subblyCart.state.currencyCode`. An empty value throws
`Cart Widget:setCurrency currency code is required`. It emits `CART_UPDATED`.

<Params>
  <Param name="currencyCode" required type="string">
    ISO 4217 code, such as `GBP`. It must be one of
    `subblyCart.shop.currencies[].abbreviation`.
  </Param>
</Params>

<Returns type="Promise<SubblyCart>">
The updated cart.
<Properties label="cart" collapsed>
  <SubblyCartFields />
</Properties>
</Returns>

<Example>

```js title="Switch to pounds"
const cart = await subblyCart.setCurrency('GBP')
```

</Example>

</Method>

<Method id="reset-cart" signature="subblyCart.resetCart()">

Discards the cart and creates an empty one with the same currency. The new cart
ID replaces the old one in the `subbly_cart_id` cookie. Any open customization
view is unmounted. It emits `CART_UPDATED`, then `CART_RESET`.

<Returns type="Promise<SubblyCart>">
The new cart.
<Properties label="cart" collapsed>
  <SubblyCartFields />
</Properties>
</Returns>

<Example>

```js title="Start a new cart"
const cart = await subblyCart.resetCart()
```

</Example>

</Method>

<Method id="set-settings" signature="subblyCart.setSettings(settings)">

Overrides the shop's cart settings at runtime: colours, typography, layout and
behaviour. Keys you omit keep their value. The widget recomputes the CSS
custom properties on its container, and loads the Google font named by
`fontStyle` by adding a `<link>` to the head. Nothing is stored, so a page
reload brings the shop's own settings back.

<Params>
  <Param name="settings" required type="Partial<ShopCartSettings>">
    The settings to change.

    <Properties label="settings" collapsed>
      <ShopCartSettingsFields />
    </Properties>
  </Param>
</Params>

<Returns type="void">
The method runs asynchronously and returns nothing worth waiting for.
</Returns>

<Example>

```js title="Restyle the widget"
subblyCart.setSettings({
  accentColor: '#701eff',
  fontStyle: 'Inter',
  buttonStyle: 24,
  afterItemAdded: 'checkout'
})
```

</Example>

</Method>

<Method id="configure-gift" signature="subblyCart.configureGift(payload, mount)">

Mounts the gift view, now or after the current customization. It does not open
the widget on its own.

<Params>
  <Param name="payload" required type="null">
    Pass `null`. Any other value logs `Payload is not supported yet in the
    configureGift method.` and is ignored.
  </Param>
  <Param name="mount" required type="boolean">
    `true` mounts the gift view now. `false` queues it, so it appears once the
    current customization view closes.
  </Param>
</Params>

<Returns type="void" />

<Example>

```js title="Ask for gift details after the item"
const { finalized } = await subblyCart.configureItem({ productId: 555 })

subblyCart.configureGift(null, finalized)
```

</Example>

</Method>

<Method id="set-customer-data" signature="subblyCart.setCustomerData(data)">

Prefills the checkout with an email and consent flags for a guest. The call
does nothing when a customer is already signed in. The same data can come from
the `email`, `marketingConsent` and `tosConsent` query parameters.

<Params>
  <Param name="data" required type="object">
    What to prefill.

    <Properties label="data">
      <Param name="email" required type="string">
        Email address for the checkout field.
      </Param>
      <Param name="marketingConsent" optional type="boolean">
        `true` ticks the marketing opt-in.
      </Param>
      <Param name="tosConsent" optional type="boolean">
        `true` ticks the terms checkbox.
      </Param>
    </Properties>
  </Param>
</Params>

<Returns type="void" />

<Example>

```js title="Prefill the checkout"
subblyCart.setCustomerData({
  email: 'ada@example.com',
  marketingConsent: true
})
```

</Example>

</Method>

<Method id="authenticate" signature="subblyCart.authenticate(accessToken?)">

Signs a customer in with a Subbly access token. The widget checks the token
against the API first. A token you pass is written to the `subbly_access_token`
cookie; a token that fails the check is cleared. On success the widget fetches
the customer and emits `SIGN_IN`.

<Params>
  <Param name="accessToken" optional type="string">
    A customer access token. Without it the widget checks the token in the
    `subbly_access_token` cookie.
  </Param>
</Params>

<Returns type="Promise<void>">
The promise rejects with `Cart Widget:authenticate Authentication failed. The
provided token is invalid or expired.` when the token does not check out.
</Returns>

<Example>

```js title="Sign a customer in"
await subblyCart.authenticate(myAccessToken)
```

</Example>

</Method>

<Method id="sign-out" signature="subblyCart.signOut()">

Signs the current customer out of the widget. It clears the widget's sign-in
state and the `subbly_access_token` cookie, and emits `SIGN_OUT`. The cart
stays as it is.

<Returns type="Promise<void>" />

<Example>

```js title="Sign the customer out"
await subblyCart.signOut()
```

</Example>

</Method>

<Method id="reload" signature="subblyCart.reload(cartId?)">

Re-reads the cart and the sign-in state from the API. Use it on a page that
rebuilds the cart itself, such as your own checkout. The widget re-checks the
shared access token and signs itself out when the token is gone, without
touching the cart; a failed check is ignored and the cart still reloads. It
emits `CART_UPDATED`.

<Params>
  <Param name="cartId" optional type="string">
    Point the widget at this cart, as a UUID, and write it to the
    `subbly_cart_id` cookie for 365 days. Without it the widget reloads the
    cart it already holds.
  </Param>
</Params>

<Returns type="Promise<void>" />

<Example>

```js title="Adopt a cart made outside the widget"
await subblyCart.reload('14920f9c-f261-4879-a3a9-e1b75993eeed')
```

</Example>

</Method>

<Method id="configure" signature="subblyCart.configure(config)">

Merges new URL configuration into the running widget, and checks the current
URL against it at once. Each field merges over the current value, so a field
you leave out keeps what the config set at init. `disableUrls` keeps the list
the widget already holds: it is not rebuilt from the new `checkoutUrl` or
`cartSummaryUrl`. Only an empty list falls back to those URLs.

<Params>
  <Param name="config" required type="object">
    The URL configuration to merge.

    <Properties label="config">
      <Param name="checkoutUrl" optional type="string">
        Page on your site that owns the checkout. The widget sends the customer
        here instead of opening its own checkout. It accepts `/checkout` and
        `/checkout/{cartId}`; a plain path gets the current cart ID appended.
      </Param>
      <Param name="cartSummaryUrl" optional type="string">
        Page on your site that owns the cart summary. It behaves like
        `checkoutUrl`.
      </Param>
      <Param name="disableUrls" optional type="string[]">
        Paths on which the widget stands down. `*` matches inside one path
        segment and `**` across segments. An entry without a wildcard also
        matches on a segment boundary, so `/checkout` covers
        `/checkout/success` but not `/checkout-foo`. Only the pathname is
        matched, case-insensitively; the query and the hash are ignored.
      </Param>
    </Properties>
  </Param>
</Params>

<Returns type="void" />

<Example>

```js title="Hand checkout to your own page"
subblyCart.configure({
  checkoutUrl: '/checkout',
  disableUrls: ['/checkout', '/cart']
})
```

</Example>

</Method>

<Method id="disable" signature="subblyCart.disable()">

Makes the widget stand down: no panel, no cart button, no checkout. The
override sits on top of the URL rules, so the widget stays disabled while
either this override is set or the URL matches a `disableUrls` pattern.

Older deployments may not have the method yet, so call it with optional
chaining.

<Returns type="void" />

<Example>

```js title="Stand the widget down"
window.subblyCart?.disable?.()
```

</Example>

</Method>

<Method id="enable" signature="subblyCart.enable()">

Clears the disable override. The widget comes back only when the current URL no
longer matches a disable pattern. A widget that first mounted disabled runs its
one-time start-up now.

<Returns type="void" />

<Example>

```js title="Bring the widget back"
window.subblyCart?.enable?.()
```

</Example>

</Method>

<Method id="initialize" signature="SubblyCart.initialize(config)">

Boots the widget by hand. You need it only with `init: false`, and you call it
on the class at `window.SubblyCart`, not on an instance.

It creates the container element, starts the SDK, mounts the app, applies the
settings, and emits `CART_READY`. It does not set a global: the name in
`globalName` is only assigned on the automatic path, so keep the instance the
promise gives you.

<Params>
  <Param name="config" required type="CartWidgetConfig">
    The same object you would put in `window.subblyConfig`. See
    [Configuration keys](/reference/cart-widget/settings#config) for every key.
  </Param>
</Params>

<Returns type="Promise<CartWidget>">
The widget instance: the object every method and property on these pages
belongs to.
</Returns>

<Example>

```js title="Initialize manually"
window.addEventListener('subbly-cart-loaded', async () => {
  const cart = await window.SubblyCart.initialize({
    apiKey: 'YOUR_STOREFRONT_API_KEY'
  })

  cart.open()
})
```

</Example>

</Method>
