ShipFree provides seamless integration with Stripe for handling payments, subscriptions, and billing.
Add your Stripe credentials to .env
:
STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_...
Handle one-time payments:
import { stripe } from "@/lib/stripe";
// Create a payment intent
const paymentIntent = await stripe.paymentIntents.create({
amount: 2000, // Amount in cents
currency: "usd",
payment_method_types: ["card"],
});
// Confirm payment
const confirmedPayment = await stripe.paymentIntents.confirm(paymentIntent.id, {
payment_method: "pm_card_visa",
});
Handle recurring payments:
// Create a subscription
const subscription = await stripe.subscriptions.create({
customer: customerId,
items: [{ price: "price_H5ggYwtDq4fbrJ" }],
payment_behavior: "default_incomplete",
payment_settings: { save_default_payment_method: "on_subscription" },
expand: ["latest_invoice.payment_intent"],
});
// Update subscription
await stripe.subscriptions.update(subscriptionId, {
items: [{ price: "price_new" }],
});
// Cancel subscription
await stripe.subscriptions.del(subscriptionId);
Process Stripe events:
// app/api/webhooks/stripe/route.ts
import { stripe } from "@/lib/stripe";
import { headers } from "next/headers";
export async function POST(req: Request) {
const body = await req.text();
const signature = headers().get("Stripe-Signature") as string;
let event;
try {
event = stripe.webhooks.constructEvent(
body,
signature,
process.env.STRIPE_WEBHOOK_SECRET!
);
} catch (err) {
return new Response(
`Webhook Error: ${err instanceof Error ? err.message : "Unknown Error"}`,
{ status: 400 }
);
}
try {
switch (event.type) {
case "customer.subscription.created":
await handleSubscriptionCreated(event.data.object);
break;
case "customer.subscription.updated":
await handleSubscriptionUpdated(event.data.object);
break;
case "customer.subscription.deleted":
await handleSubscriptionDeleted(event.data.object);
break;
// Add more event handlers as needed
}
return new Response(null, { status: 200 });
} catch (error) {
console.error("Error processing webhook:", error);
return new Response("Webhook handler failed", { status: 500 });
}
}
import { Elements } from "@stripe/stripe-js";
import { PaymentElement } from "@stripe/stripe-js/elements";
export function CheckoutForm() {
return (
<Elements stripe={stripePromise}>
<form>
<PaymentElement />
<button type="submit">Pay Now</button>
</form>
</Elements>
);
}
export function SubscriptionManager() {
return (
<div>
<h2>Manage Subscription</h2>
<div className="space-y-4">
<PlanSelector />
<PaymentMethodSelector />
<BillingDetails />
</div>
</div>
);
}
Security
User Experience
Testing
Important events to handle:
Subscription Events
customer.subscription.created
customer.subscription.updated
customer.subscription.deleted
Payment Events
payment_intent.succeeded
payment_intent.payment_failed
charge.succeeded
charge.failed
Customer Events
customer.created
customer.updated
customer.deleted
Common errors and solutions:
try {
// Stripe operation
} catch (error) {
if (error instanceof Stripe.errors.CardError) {
// Handle card errors
} else if (error instanceof Stripe.errors.InvalidRequestError) {
// Handle invalid parameters
} else if (error instanceof Stripe.errors.AuthenticationError) {
// Handle authentication errors
} else {
// Handle unexpected errors
}
}
const TEST_CARDS = {
success: "4242424242424242",
decline: "4000000000000002",
insufficient_funds: "4000000000009995",
requires_authentication: "4000002500003155",
};
stripe listen --forward-to localhost:3000/api/webhooks/stripe