Stripe
Stripe is the primary payment account behind Plandalf offers. Plandalf orchestrates checkout, products, prices, invoices, coupons, bumps, and automation context while money still lands in your Stripe account.
Note. Plandalf is payment-adjacent Your Stripe account remains the processor account of record. Plandalf reads and writes through Stripe; it does not become your merchant of record.
The path
-
Connect the Stripe account
Open the Stripe integration from the Plandalf dashboard and authorize the account that should receive payments.
-
Import or create products and prices
Use existing Stripe products/prices when they already exist, or create Plandalf products and prices for the offer.
-
Attach Stripe-backed prices to the offer
Put the products, prices, bumps, variants, coupons, and payment options into the checkout flow.
-
Run a test mode link
Connect and verify the organization’s Stripe test integration, then use a test checkout link before live traffic. Confirm the actual provider environment before attempting payment.
What Stripe owns
Stripe-owned records
-
Account(Stripe account): The connected account that receives payments and owns Stripe-side payment records. -
Customers(Stripe customer): Checkout can create or resolve customers through the connected Stripe account. -
Products and prices(Stripe objects): Existing Stripe prices can prefill Plandalf price fields when a product is connected. -
Payment attempts(Stripe payment): Card payments run through Stripe and should be tested before the offer goes live.
What Plandalf owns
Offer flow
Pages, checkout blocks, order bumps, upsells, forms, redirects, and completion behavior.
Commerce context
The offer, customer, product, line item, coupon, invoice, deadline, and automation context that travels with the purchase.
Testing path
Test links let the team rehearse checkout without taking live payments.
Post-purchase work
Webhooks and Sequences can react after checkout using the structured purchase context.
Product and price import
When a product is connected to Stripe, Numi can search Stripe prices and prefill the local price form. This helps keep amount, currency, recurring interval, and price naming aligned with Stripe without retyping everything by hand.
Tip. Use Stripe as the payment truth, not the checkout UI Stripe owns the payment records. Plandalf owns the offer surface, checkout flow, and follow-up context around that payment.
Test before live traffic
Warning. A test URL is not enough The current checkout session-creation path can fall back to the live Stripe integration when the organization’s test integration is missing. Do not attempt a payment based only on a test label or SDK mode option. Verify the connected test integration and the actual Stripe account, keys, customer, and prices first.
-
Open the test link
Use the test mode link from the offer share screen.
-
Complete checkout with a Stripe test card
Confirm the payment path without creating a real charge.
-
Check the completed session
Verify selected products, bumps, coupons, invoices, redirects, and automation triggers.
-
Switch to live only after the flow is clean
Test mode should catch pricing and checkout errors before customers see them.
Existing subscriptions and v1 migration
Keep existing customer and subscription associations when replacing a paywall. The current backend derives an upgrade intent when it receives an existing subscription association, and sends an upgrade session through subscription-change preview and commit. A generic recurring purchase without that association can instead create a new subscription.
For an existing-subscription route, your backend signs customer identity with customer and subscription claims; see the v1 migration example. These identifiers must belong to the authenticated account, the selected service, and the connected Stripe environment. A subscription claim does not select the target price or implement an arbitrary add-on purchase.
Read the current provider items on your backend before building your tier and price context. This example assumes stripe is already configured for the correct provider account/environment and expected was loaded from the authenticated application’s records—not supplied by the browser.
export async function readStripeItems(stripe, expected) {
const subscription = await stripe.subscriptions.retrieve(expected.subscriptionId);
const customerId = typeof subscription.customer === "string"
? subscription.customer : subscription.customer.id;
if (customerId !== expected.customerId) throw new Error("Customer mismatch");
if (subscription.livemode !== expected.livemode) throw new Error("Environment mismatch");
if (subscription.items.has_more) throw new Error("Load every subscription item first");
return {
subscriptionId: subscription.id,
status: subscription.status,
items: subscription.items.data.map(item => ({
itemId: item.id,
priceId: item.price.id,
quantity: item.quantity,
amount: item.price.unit_amount,
currency: item.price.currency,
interval: item.price.recurring?.interval,
intervalCount: item.price.recurring?.interval_count
}))
};
}Set expected.livemode explicitly (false for testing). This helper rejects a partial item list; implement provider pagination before supporting larger subscriptions. Map each provider price ID through your known service/tier/add-on catalog; do not infer a tier from an amount or only inspect the first item. Tiered or metered prices can require additional pricing data; unit_amount is not universally a complete price model. See Stripe’s Subscription reference.
Before enabling a migration route, prove that:
- The intended existing subscription item changes; a base-plan swap and adding an add-on are distinct operations.
- Unrelated subscription items survive, and the intended quantity is retained or changed explicitly.
- The preview and committed payment agree on proration, effective date, trials, discounts, and billing cycle.
- Failed or action-required payment does not grant paid access early.
- No duplicate subscription or repeat charge is created, including repeated requests.
The presence of a Stripe change adapter is not proof every add-on or legacy plan is supported. Keep unverified routes on your existing flow. Stripe’s own price-change guidance explains item replacement, quantities, and payment handling.
Keep server-side subscription events
Retain your provider webhook receiver and idempotent price-to-entitlement reconciliation. Verify signatures using the raw request body and the correct endpoint secret; see Stripe signature verification.
Provider event
Application responsibility
invoice.paid
Reconcile payment and subscription state before granting eligible access.
invoice.payment_failed / invoice.payment_action_required
Apply your existing payment-recovery/access policy; do not count checkout UI completion as payment.
customer.subscription.created / updated / deleted
Reconcile current items, tier, quantity, and cancellation state.
Handle retries and events arriving out of order. Browser SDK events and Plandalf HTTP actions are separate signals; they do not replace this receiver. See Stripe’s subscription webhook guidance.
Related docs
-
Create an offer — Attach products, prices, layout, and test sessions to a checkout flow.
-
Pricing models — Model one-time, subscription, tiered, trial, and promo-aware prices.
-
Webhooks — Send purchase context into your backend and automation stack.