Learn Stripe: Easy Steps for Card Payments and Subscription Management

Learn Stripe: Easy Steps for Card Payments and Subscription Management

A beginners guide to stripe SDK. Building native in app stripe checkout flows on the web

Stripe is a widely-used payment processing platform that offers a range of tools and features to facilitate online transactions.

here's a detailed guide for a technical blog on implementing Stripe payments using cURL, covering registering customers, processing card payments using Stripe Elements, and setting up subscriptions.

Registering Customers with Stripe

Before billing a customer, you need to create a Customer object that you can configure with a name, email, and payment method. First, you need to register customers in Stripe. This involves creating a customer object on Stripe's servers. This entails sending to stripe a Customer object in your API request. This object represents a customer of your business. Use it to create recurring charges and track payments that belong to the same customer.

Creating a Customer:

curl https://api.stripe.com/v1/customers \
  -u "sk_test_4eC39HqLyjWDarjtT1zdp7dc:" \
  -d name="Jenny Rosen" \
  --data-urlencode email="jennyrosen@example.com"

The Customer resource is a core entity within Stripe. From the json response, store the customer_id in your database against your user and use it to store all of the profile, billing, and tax information required to bill a customer for subscriptionsand one-off invoices.

Considerations:

  • You should have your own user table in your database, where you also store the customer_id (returned in the response when creating a customer object), used to manage customers on Stripe's platform.

  • Stripe does not check for repeated E-Mails, so you must do so (there can be more than one customer with the same E-Mail on Stripe's platform).

Handling Card Payments with Stripe Elements

Stripe Elements is a set of prebuilt UI components that helps you create your own checkout flows. Choose the Elements you need and match them to the look and feel of your site with CSS-level styling.

To handle card payments, you need to create a Payment Intent and collect payment information using Stripe Elements. A PaymentIntent is used in the process of collecting a payment from a customer. It tracks the payment's lifecycle and can go through multiple statuses. It is recommended to create exactly one PaymentIntent for each order or customer session. The PaymentIntent encapsulates details about the transaction, such as the supported payment methods, the amount to collect, and the desired currency. You can learn more about Payment Intents in the Payment Intents API

Creating a Payment Intent:

curl https://api.stripe.com/v1/payment_intents \
  -u sk_test_your_secret_key: \
  -d amount=2000 \
  -d currency="usd" \
  -d "payment_method_types[]"="card"

After the PaymentIntent is created, attach a payment method and confirm to continue the payment.

When you use confirm=true during creation, it’s equivalent to creating and confirming the PaymentIntent in the same call. You can use any parameters available in the confirm API when you supply confirm=true.

Integrating Stripe Payment Element

The Payment Element is a UI component for the web that accepts 40+ payment methods, validates input, and handles errors. Use it alone or with other elements in your web app’s frontend.

In your HTML, include Stripe.js and create the payment form:

<!DOCTYPE html>
<html>
<head>
  <title>Checkout</title>
  <script src="https://js.stripe.com/v3/"></script>
</head>
<body>
  <form id="payment-form">
    <div data-element='stripe'></div>
    <div data-element='stripe-address'></div>
    <button id="submit">Pay</button>
  </form>

  <script>

    const stripe = new window.Stripe("pk_test_your_publishable_key");

var appearance = {
  //theme: "stripe",
  labels: "floating",
  variables: {
    colorPrimary: "#152317",
    colorBackground: "#ffffff",
    colorText: "#656762",
    colorDanger: "#df1b41",
    fontFamily: "Ideal Sans, system-ui, sans-serif",
    spacingUnit: "6px",
    borderRadius: "16px",
    gridColumnSpacing: "12px",
    gridRowSpacing: "12px",
    // See all possible variables below
  },
  rules: {
    ".Tab": {
      border: "1px solid #152317",
      boxShadow: 'inset 0 0 0 1px #bfc2bc',
    },
    ".TabIcon:disabled": {
      border: "1px solid #152317",
      boxShadow: 'inset 0 0 0 1px #bfc2bc',
    },
    ".Input": {
      //boxShadow: 'inset 0 0 0 1px #bfc2bc',
      border: "1px solid #BFC2BC",
      boxShadow: 'none',
      padding: "16px 24px 16px 24px",
      fontSize: "18px",
      fontWeight: "400",
      letterSpacing: "-2%",
      color: "#152317",
      //fontFamily: "Geist",
      lineHeight: "150%",
    },
    ".Input:focus": {
      //boxShadow: 'inset 0 0 0 2px #152317',
      boxShadow: 'none',
      border: "2px solid #152317"
    },
    ".Label": {
      fontSize: "18px",
      fontWeight: "500",
      letterSpacing: "-2%",
      color: "#656762",
      lineHeight: "150%",
      //fontFamily: "Geist",
      // Adjusted to a valid font weight
    },
     ".Label:focus": {
      color: "#656762",

    },
  },
};
const options = {
  appearance: appearance,
  clientSecret: v.client_secret,

}
const elements = stripe.elements(options);
const addressElement = elements.create("address", {
  mode: "billing",
  allowedCountries: ["US"],
  blockPoBox: true,
  fields: {
    phone: "never",
  },
});
const paymentElement = elements.create("payment", {
  layout: {
    type: "accordion",
    defaultCollapsed: false,
    radios: true,
    spacedAccordionItems: false,
  },
});
paymentElement.mount(document.querySelector("[data-element='stripe']"));
addressElement.mount(document.querySelector("[data-element='stripe-address']"));

//initially disable the submit
v.purchase_service = {
  payment: false,
  address: false,
};

//listen for change events, enable|disable submit btn
paymentElement.on("change", (event) => {
  if (event.complete) {
    v.purchase_service.payment = true;
  } else {
    v.purchase_service.payment = false;
  }
});
addressElement.on("change", (event) => {
  console.log(event.complete);
  if (event.complete) {
    v.purchase_service.address = true;
  } else {
    v.purchase_service.address = false;
  }
});

const myform = document.querySelector("[wized='pay_form']");
myform.addEventListener("submit", async function (event) {
  event.preventDefault();
  console.log("confirming payment");
  stripe
    .confirmPayment({
      elements,
      confirmParams: {
        return_url: "https://example.io/paywall/thank-you",
      },
    })
    .then((result) => {

      if (result.error) {
        console.log(result.error);

      } else {
        if (result.paymentIntent && result.payment.status == "succeeded") {
            //subscribe the user in the webhook post process
        }
      }
    });
});

  </script>
</body>
</html>

Validating the payment form

You might want to check if all the card input or the addresses are filled out correctly. The only way to communicate with your Element is by listening to an event. An Element might emit various events. You would listen to the change event on an Element, and then check for the complete field on the event handler object

//listen for change events, enable|disable submit btn
paymentElement.on("change", (event) => {
  if (event.complete) {
    enable_submit_btn = true;
  } else {
    enable_submit_btn = false;
  }
});
addressElement.on("change", (event) => {
  console.log(event.complete);
  if (event.complete) {
    enable_submit_btn = true;
  } else {
    enable_submit_btn = false;
  }
});

Confirming the payment

Use stripe.confirmPayment like in above example to confirm a PaymentIntent using data collected by the Payment Element, or with manually provided data via confirmParams. When called, stripe.confirmPayment will attempt to complete any required actions, such as authenticating your user by displaying a 3DS dialog or redirecting them to a bank authorization page. Your user will be redirected to the return_url you pass once the confirmation and charge is complete.

Payment status updates

Monitor and verify payment status server side, so that you can respond to successful and failed payments. You'd setup an endpoint on your server and set it up in stripe dashboard. Stripe can send webhook events to your server to notify you when the status of a PaymentIntent changes, which you can use for purposes such as determining when to fulfill goods and services.

Don’t attempt to handle order fulfillment on the client side because customers can leave the page after payment is complete but before the fulfillment process initiates. Instead, use webhooks to monitor the payment_intent.succeeded event and handle its completion asynchronously instead of attempting to initiate fulfillment on the client side.

To handle a webhook event, create a route on your server and configure a corresponding webhook endpoint in the Dashboard. Stripe sends the payment_intent.succeeded event when a payment succeeds, and the payment_intent.payment_failed event when a payment fails. Webhooks are documented well in another part of this article

Setting Up Subscriptions

Subscriptions allow you to charge a customer on a recurring basis. To create a subscription, you need to go to your stripe dashboard to set up a product and price in Stripe, then subscribe the customer to that product.

Products describe the specific goods or services you offer to your customers. For example, you might offer a Standard and Premium version of your goods or service; each version would be a separate Product.

Prices define the unit cost, currency, and (optional) billing cycle for both recurring and one-time purchases of products.

Creating a Subscription:

curl https://api.stripe.com/v1/subscriptions \
  -u sk_test_your_secret_key: \
  -d customer=cus_J2GpG3L2uVkJvZ \
  -d items[0][price]=price_1Hh1YqF5QGhz0B2xNn1eJ2Wm

Setting Up Subscriptions schedules

Subscription Schedules in Stripe allow you to manage subscriptions with custom billing cycles, phases, and other configurations. This is useful for offering trials, discounts, or other promotional periods. Here, we'll go through how to create, update, and manage subscription schedules using cURL commands.

Creating a Subscription Schedule

To create a subscription schedule, you need to define the customer, start date, and phases. Each phase can have different billing intervals, prices, and trial periods.

I once had a unique case where i was asked to create a checkout flow where the user made a one time payment of 129$ and continuing subscription of 49$ each year. I used Stripe's payment element and payment intent to collect 129$ from the card and subscption schedule to setup a deferred subscription that starts billing the client after a year.

Example: Creating a Subscription Schedule

curl https://api.stripe.com/v1/subscription_schedules \
  -u sk_test_your_secret_key: \
  -d customer=cus_J2GpG3L2uVkJvZ \
  -d start_date=now \
  -d "phases[0][items][0][price]"="price_1Hh1YqF5QGhz0B2xNn1eJ2Wm" \
  -d "phases[0][items][0][quantity]"=1 \
  -d "phases[0][iterations]"=3 \
  -d "phases[0][trial_end]"=1672531199 \
  -d "phases[1][items][0][price]"="price_1Hh2XqF5QGhz0B2xXn1eJ2Wm" \
  -d "phases[1][items][0][quantity]"=1 \
  -d "phases[1][iterations]"=12

Breaking Down the Parameters

  • customer: The customer ID to whom the subscription schedule belongs.

  • start_date: When the subscription should start. Use now for immediate start or Unix a timestamp to backdate the subscription so that it starts on a past date, or set a future date for the subscription to start on.

  • phases: An array defining each phase of the subscription.

Phase Parameters

  • items: The subscription items for this phase.

    • price: The price ID for the subscription item.

    • quantity: Quantity for the item.

  • iterations: Number of billing cycles for this phase.

  • trial_end: Unix timestamp for when the trial period ends.

Differences Between Subscriptions and Subscription Schedules

Subscriptions

Definition: Recurring payment plans with a fixed billing cycle.

Use Case:

  • Simple, ongoing billing.

  • Consistent billing amount and cycle (e.g., monthly, yearly).

Key Features:

  • Fixed billing frequency.

  • Trials.

  • Automatic billing.

  • Easy management.

Subscription Schedules

Definition: Flexible billing configurations with multiple phases.

Use Case:

  • Complex billing needs with changing terms over time.

  • Initial discounts, promotional pricing, phased pricing models.

Key Features:

  • Multiple phases.

  • Custom billing cycles per phase.

  • Automatic transitions between phases.

  • Greater flexibility.

When to Use

  • Use Subscriptions: For straightforward, consistent recurring billing.

  • Use Subscription Schedules: For flexible, phased billing arrangements with varying terms.

Using Webhooks for Event Handling

When building Stripe integrations, you might want your applications to receive events as they occur in your Stripe accounts, such as when a customer’s bank confirms a payment, a customer disputes a charge, a recurring payment succeeds, or when collecting subscription payments. So that your backend systems can execute actions accordingly.

To enable webhook events, you need to register webhook endpoints via the Dashboard or API. After you register them, Stripe can push real-time event data to your application’s webhook endpoint when events happen in your Stripe account. Stripe uses HTTPS to send webhook events to your app as a JSON payload that includes an Event object.

Setting Up a Webhook Endpoint via Dashboard

Setting Up a Webhook Endpoint via API

curl https://api.stripe.com/v1/webhook_endpoints \
  -u sk_test_your_secret_key: \
  -d url="https://example.com/my/webhook/endpoint" \
  -d "enabled_events[]"="checkout.session.completed"

Sample Webhook Endpoint (Node.js/Express):

const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const stripe = require('stripe')('sk_test_your_secret_key');

app.use(bodyParser.json());

app.post('/my/webhook/endpoint', (request, response) => {
  const sig = request.headers['stripe-signature'];
  let event;

  try {
    event = stripe.webhooks.constructEvent(
      request.body,
      sig,
      'whsec_YOUR_WEBHOOK_SECRET'
    );
  } catch (err) {
    return response.status(400).send(`Webhook Error: ${err.message}`);
  }

  if (event.type === 'checkout.session.completed') {
    const session = event.data.object;
    // Handle successful checkout session
  }

  response.status(200).end();
});

app.listen(3000, () => console.log('Running on port 3000'));

Listen for events

Events are Stripe's way of letting you know when something interesting happens in your account. For example, when a charge succeeds, we a charge.succeeded event is created, and when an invoice payment attempt fails, an invoice.payment_failed event is created. Certain API requests might create multiple events. For example, if you create a new subscription for a customer, you receive both a customer.subscription.created event and a charge.succeeded event.

When setting up your webhook add a list of events you want to receive for that endpoint so when certain actions occur in your account, such as successful payments, subscription renewals, and payment failures your application to respond to these events in real-time, automating processes and ensuring seamless payment management.

Payment Intents Webhook

https://api.example.com/v1/stripe/webhook/payment_intents

To effectively manage the lifecycle of a payment using Stripe's Payment Intents API, you should listen to several key events. These events will help you monitor the progress and outcome of each payment attempt. Here are the primary events you should consider:

  1. payment_intent.created

  2. payment_intent.succeeded

  3. payment_intent.payment_failed

  4. payment_intent.canceled

  5. payment_intent.processing

  6. payment_intent.requires_action

Event Descriptions

  1. payment_intent.created

    • Description: This event is triggered when a PaymentIntent is created.

    • Use Case: You might use this to log or track when a payment process starts.

  2. payment_intent.succeeded

    • Description: This event is triggered when a PaymentIntent has successfully completed payment.

    • Use Case: Use this to confirm the payment, fulfill orders, send confirmation emails, or update your database to reflect the successful transaction.

  3. payment_intent.payment_failed

    • Description: This event is triggered when a PaymentIntent fails to complete payment.

    • Use Case: Handle this event to notify the user of the failure, log the failure for review, or prompt the user to retry the payment with different payment information.

  4. payment_intent.canceled

    • Description: This event is triggered when a PaymentIntent is canceled.

    • Use Case: You can use this event to clean up any reserved resources or update the order status to canceled in your system.

  5. payment_intent.processing

    • Description: This event is triggered when a PaymentIntent is processing.

    • Use Case: Useful for tracking when a payment is being processed but is not yet complete. You might inform the user that the payment is underway.

  6. payment_intent.requires_action

    • Description: This event is triggered when a PaymentIntent requires additional user action to complete.

    • Use Case: Implement logic to prompt the user for further authentication steps, such as 3D Secure authentication.

Subscriptions Webhook

https://api.example.com/v1/stripe/webhook/subscriptions

To manage subscriptions effectively using Stripe, you need to listen for several key events that cover the lifecycle of a subscription. This includes creation, updates, payment successes, payment failures, and cancellations. Here are the primary subscription-related events you should consider:

  1. customer.subscription.created

  2. customer.subscription.updated

  3. customer.subscription.deleted

  4. invoice.payment_succeeded

  5. invoice.payment_failed

  6. invoice.upcoming

  7. invoice.finalized

  8. invoice.marked_uncollectible

  9. invoice.paid

  10. invoice.payment_action_required

Event Descriptions

  1. customer.subscription.created

    • Description: This event is triggered when a new subscription is created.

    • Use Case: Use this to initiate any setup processes, welcome emails, or initial configurations for the new subscriber.

  2. customer.subscription.updated

    • Description: This event is triggered when a subscription's details are updated.

    • Use Case: Use this to handle changes in subscription plans, billing cycles, or any other subscription parameters.

  3. customer.subscription.deleted

    • Description: This event is triggered when a subscription is canceled or deleted.

    • Use Case: Use this to handle cleanup tasks, notify the user, or update the user's access status.

  4. invoice.payment_succeeded

    • Description: This event is triggered when an invoice payment succeeds.

    • Use Case: Use this to confirm the payment, extend the subscription period, or send a confirmation email.

  5. invoice.payment_failed

    • Description: This event is triggered when an invoice payment fails.

    • Use Case: Use this to notify the user of the failed payment, prompt them to update their payment information, or log the failure for further analysis.

  6. invoice.upcoming

    • Description: This event is triggered a few days before an invoice is due.

    • Use Case: Use this to remind the user of the upcoming payment and ensure they have sufficient funds or update their payment method if needed.

  7. invoice.finalized

    • Description: This event is triggered when an invoice is finalized.

    • Use Case: Use this to prepare for the payment collection process.

  8. invoice.marked_uncollectible

    • Description: This event is triggered when Stripe marks an invoice as uncollectible.

    • Use Case: Use this to mark the invoice as bad debt in your accounting system or take further actions to recover the payment.

  9. invoice.paid

    • Description: This event is triggered when an invoice is successfully paid.

    • Use Case: Use this to update the user's subscription status or extend their access period.

  10. invoice.payment_action_required

  • Description: This event is triggered when an invoice requires additional user actions to complete the payment (e.g., 3D Secure authentication).

  • Use Case: Use this to prompt the user to complete the necessary actions to finalize the payment.

Subscriptions Schedule Webhooks

Stripe's Subscription Schedules feature allows you to create and manage subscriptions with custom billing cycles and phases. This is particularly useful for scenarios like trial periods, discounts, or other promotional offers that change over time. Here’s how you can manage Subscription Schedules using Stripe, including setting up a webhook to handle related events.

Key Events for Subscription Schedules

To effectively manage Subscription Schedules, you should listen to several key events:

  1. subscription_schedule.created

  2. subscription_schedule.updated

  3. subscription_schedule.released

  4. subscription_schedule.canceled

  5. subscription_schedule.completed

Event Descriptions

  1. subscription_schedule.created

    • Description: Triggered when a new Subscription Schedule is created.

    • Use Case: Use this event to initialize any necessary resources or records associated with the schedule.

  2. subscription_schedule.updated

    • Description: Triggered when a Subscription Schedule is updated.

    • Use Case: Handle changes to the schedule, such as phase updates or billing interval changes.

  3. subscription_schedule.released

    • Description: Triggered when a Subscription Schedule is released.

    • Use Case: Handle the transition of the subscription from being scheduled to an active subscription.

  4. subscription_schedule.canceled

    • Description: Triggered when a Subscription Schedule is canceled.

    • Use Case: Clean up resources, notify the customer, or handle any cancellation logic.

  5. subscription_schedule.completed

    • Description: Triggered when a Subscription Schedule completes all phases.

    • Use Case: Handle any end-of-schedule logic, such as renewing the subscription or offering new plans.

Conclusion

This guide provides a comprehensive overview of setting up Stripe for customer registrations, handling card payments using Stripe Elements, creating subscriptions, and using webhooks to handle events. With these steps, you can integrate Stripe into your application to manage various payment needs efficiently.

Feel free to customize the code snippets and adapt them to your specific use case. Happy coding!