ShipNowKit ships a payment module that supports Stripe and Paddle for one-time purchases and subscriptions. Users can stay signed in while checking out, switching plans, and viewing order summaries.
What’s included
- Provider switch: Pick Stripe or Paddle in Dashboard “Payment Settings”; store public keys, secrets, and webhook secrets separately.
- Price mapping: Use
priceMappingsto map business keys (e.g.,PRO_MONTHLY) to provider priceIds; frontend reads withgetPriceId. - Pricing UI:
PriceBtnhandles login guard + checkout/upgrade flows;usePriceAmountsbatches price fetch;useCheckout/useChangeSubscriptionPlanhandle purchase vs. plan change.
Quick setup
Go to Dashboard > Settings > Payment, choose Stripe or Paddle, and save.
Stripe: stripe.publicKey, stripe.secretKey, stripe.webhookSecret (optional stripe.billingPortalConfigId).
Paddle: paddle.clientToken, paddle.apiKey, paddle.webhookSecret, paddle.environment.
Add mappings in “Price Mappings”, e.g., PRO_MONTHLY -> price_123, then the frontend can read them.
paymentConfig.successPage defaults to /payment/success; pricing section anchor is /#pricing; adjust as needed.
Tip:
getPriceId('PRO_MONTHLY')reads DashboardpriceMappingsfirst, then falls back toNEXT_PUBLIC_PRO_MONTHLYenv var for local/preview overrides.
Pricing UI & Hooks
usePriceAmounts — fetch price amounts in batch
Input
Prop
Type
Output
Prop
Type
import { getPriceId } from "@/lib/client-config";
import { usePriceAmounts } from "@/components/price/hooks/usePriceAmounts";
const priceIds = ["PRO_MONTHLY", "PRO_YEARLY"]
.map((key) => getPriceId(key))
.filter(Boolean) as string[];
const { priceAmounts, isLoadingPriceAmounts } = usePriceAmounts(priceIds);useActivePlans — fetch active subscriptions / one-time purchases
Input
Prop
Type
Output
Prop
Type
- Typical uses:
- Pricing page: pass to
PriceBtnactivePlansto decide new purchase vs. change plan vs. redirect. - UI copy: show “Activated”, “Switch to annual”, etc., based on existing plans.
- Pricing page: pass to
import { useActivePlans } from "@/components/price/hooks/useActivePlans";
import { PriceBtn } from "@/components/price/PriceBtn";
import { getPriceId } from "@/lib/client-config";
const { userActiveSubscriptions, userActiveOneTimePayments } = useActivePlans(true, true);
<PriceBtn
btnText="Subscribe now"
targetPlan={{ isSubscription: true, priceId: getPriceId("PRO_MONTHLY") || undefined }}
activePlans={[...userActiveSubscriptions, ...userActiveOneTimePayments]}
/>;PriceBtn — handle login, checkout, upgrade
Prop
Type
import { PriceBtn } from "@/components/price/PriceBtn";
import { useActivePlans } from "@/components/price/hooks/useActivePlans";
const { userActiveSubscriptions } = useActivePlans(true, false);
<PriceBtn
btnText="Upgrade to Pro"
targetPlan={{
isSubscription: true,
name: "Pro",
priceId: getPriceId("PRO_MONTHLY") || undefined,
isYearly: false,
}}
activePlans={userActiveSubscriptions}
className="w-full bg-primary text-white py-3 rounded-lg"
/>;