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

# Stripe

> Connect Stripe to sell learning content — price units, take payment at checkout, and lock paid content behind a paywall.

export const RelatedPages = ({pages = []}) => {
  if (pages.length === 0) return null;
  return <>
      <br />
      <strong>Related articles</strong>
      <CardGroup cols={2}>
        {pages.map(page => <Card title={page.title} href={page.href} key={page.href} />)}
      </CardGroup>
    </>;
};

export const RoleBadge = ({roles = []}) => {
  const colorMap = {
    Admin: "yellow",
    Manager: "blue",
    Learner: "green",
    "Co-editor": "purple"
  };
  return <>
      {roles.map(role => <span key={role}><Badge color={colorMap[role] || "gray"} size="sm" shape="pill">{role}</Badge>{" "}</span>)}
    </>;
};

export const Prerequisites = ({items = []}) => {
  return <Note title="Before you start">
      {items.length > 0 && <ul>
          {items.map((item, index) => <li key={index}>{item}</li>)}
        </ul>}
    </Note>;
};

<RoleBadge roles={["Admin"]} />

Connect your organization's Stripe account to sell learning through Juno. Set a price on any unit — a Course, quiz, or Journey — and learners pay for it at Stripe Checkout. People who haven't bought a priced unit can't open it.

<Note>
  Selling with Stripe is turned on per organization. If you don't see the Stripe card, contact your Juno account team to enable it. Each organization connects its **own** Stripe account — prices and payments live in your Stripe account, not a shared one.
</Note>

<Prerequisites
  items={[
"A Stripe account with API keys",
"Selling with Stripe enabled for your organization by Juno",
"Admin access to Juno",
]}
/>

***

## Connect Stripe

Open **Settings → Integrations → Stripe** and work through the form.

<Steps>
  <Step title="Choose Test or Live mode">
    Use **Test** while you're setting things up, **Live** to take real payments. Your keys must match the mode — test keys with Test, live keys with Live.
  </Step>

  <Step title="Copy the webhook endpoint URL">
    The card shows a **webhook endpoint URL** for your organization. Copy it — you'll paste it into Stripe next.
  </Step>

  <Step title="Create the webhook in Stripe">
    In your Stripe dashboard, create a webhook endpoint pointing at that URL and subscribe to these events:

    | Event                                      | Why it's needed                                      |
    | ------------------------------------------ | ---------------------------------------------------- |
    | `checkout.session.completed`               | Completes a purchase and grants access               |
    | `checkout.session.async_payment_succeeded` | Completes delayed payment methods (e.g. bank debits) |
    | `charge.refunded`                          | Revokes access when you refund in Stripe             |

    Then copy the webhook's **signing secret**.
  </Step>

  <Step title="Enter your keys">
    Back in Juno, enter your Stripe account email, **secret key**, **publishable key**, and the **webhook signing secret**, then connect. Juno checks the key formats and verifies them with Stripe before saving. Your secret key and signing secret are stored securely and never shown back to you.
  </Step>
</Steps>

<Note>
  The connect form only prompts you to subscribe `checkout.session.completed`. Also subscribe `checkout.session.async_payment_succeeded` and `charge.refunded` so delayed payments and refunds work correctly.
</Note>

### After connecting

The card shows your account email, mode, publishable key, and the last four digits of your secret key. If your keys ever stop working, the card shows a warning with a **Reconnect** option.

To disconnect, select **Disconnect** and confirm. Learners can't buy content while you're disconnected, but your Stripe account and its products aren't deleted — reconnect any time to resume selling.

***

## Price a unit

Open a unit's settings and find the **Pricing** section.

1. Turn on the **sell** toggle.
2. Set a **price** and **currency**. Currencies that don't use decimal places (such as JPY) take whole numbers only.
3. Optionally set a **discount price** — it must be lower than the price, and shows as a struck-through original next to the discounted amount.
4. Save.

Saving creates or updates a matching product and price in your Stripe account. Changing the price later creates a new Stripe price and retires the old one, so your Stripe catalog always matches what learners see.

<Warning>
  If Juno can't sync the price to Stripe, the save is rejected so pricing never drifts out of step. If Stripe is disconnected or your keys are invalid, reconnect in Settings and save again. For a temporary Stripe error, try again in a moment.
</Warning>

To stop selling a unit, turn the sell toggle off or set the price to zero — it becomes free again and the paywall no longer applies.

***

## What learners see

On priced units, catalog cards show a **price** and a **lock**. Once a learner owns the unit, the lock turns green and open. Free content and external providers are unaffected — no price, no lock.

### Buying

Selecting **Buy** opens Stripe Checkout in a new tab, where the learner pays (promo codes are accepted). Juno waits for payment and enrols them automatically once it's complete — the learner lands on the unit, ready to start.

<Note>
  If the learner's browser blocks the new tab, Juno shows an **Open checkout** link they can select to continue.
</Note>

### The paywall

If a learner opens a link straight to a paid unit they haven't bought, they see an **Unlock this content** paywall instead of the content, with the price and a buy option. The content stays locked everywhere it could be reached — opening the unit, its content and questions, external (xAPI) launches, and public links.

***

## Access and refunds

Buying requires signing in, so a purchase is always tied to the buyer. Some people can open a paid unit without buying it:

* **Content admins** and the unit's **creator or co-editors**.
* Learners who were **assigned** the unit by an admin.

Refunds are handled in your **Stripe dashboard**. A **full refund** revokes the learner's access to the unit, but their **learning progress is kept** — if they buy again, they pick up where they left off. **Partial refunds** don't change access.

***

## Troubleshooting

| Problem                           | What to check                                                                                                                                                                  |
| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Can't save a price                | Stripe must be connected with valid keys. Reconnect in **Settings → Integrations → Stripe**, then save again.                                                                  |
| Learners can't buy                | Confirm Stripe is connected and the unit's sell toggle is on with a price above zero.                                                                                          |
| Paywall won't clear after payment | Check your Stripe webhook subscribes `checkout.session.completed` and `checkout.session.async_payment_succeeded`. Delayed payment methods complete only after the async event. |
| Refund didn't revoke access       | Only **full** refunds revoke access, and your webhook must subscribe `charge.refunded`.                                                                                        |

<RelatedPages
  pages={[
{ href: "/learning/catalog", title: "Catalogs & Marketplace" },
{ href: "/learning/units-and-courses", title: "Content Types" },
{ href: "/admin/content-management", title: "Content & Learning Management" },
]}
/>
