skip to Main Content

I am attempting to create a subscription to a product in Stripe in the same step as the user registration. I’m ultimately trying to avoid having them first create an account, and then entering their payment details once an account has been created.

However, this process is proving to be tricky. It seems that in order to create a subscription, it needs me to set up a User Intent, which provides a client_secret, which I need to pass along to Stripe in order to create a subscription on the Stripe side of things. That’s where I am getting hung up.

Right now, the payment information form looks like this:

<form id="payment-form" action="{{ route('subscription.create') }}" method="POST">
    @csrf
    <input type="hidden" name="plan" id="plan" value="{{ $plan->id }}">

    <div class="row">
        <div class="col-xl-4 col-lg-4">
            <div class="form-group">
                <label for="">Name</label>
                <input type="text" name="name" id="card-holder-name" class="form-control" value="" placeholder="Name on the card">
            </div>
        </div>
    </div>

    <div class="row">
        <div class="col-xl-4 col-lg-4">
            <div class="form-group">
                <label for="">Card details</label>
                <div id="card-element"></div>
            </div>
        </div>
       <div class="col-xl-12 col-lg-12">
           <hr>
           <button type="submit" class="btn btn-primary" id="card-button" data-secret="{{ $intent->client_secret }}">Purchase</button>
       </div>
   </div>
</form>

In that markup, if you look at the data-secret attribute of the submit button – that seems to be the hangup point for me. I need to basically take the client_secret in the intent that is created, and pass it along in my registration method – but I’m not sure how to do that.

Basically, I have the default Laravel Auth form, and am operating inside of the App/Http/Controllers/Auth/RegisteredUserController.php file, which is where the store method is that creates the user inside of the database.

Here is my code for that method:

public function store(Request $request) {
    $userID = uniqid('u-');
    $plan = Plan::find($request->plan);

    $request->validate([
        'first_name' => ['required', 'string', 'max:255'],
        'last_name' => ['required', 'string', 'max:255'],
        'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
    ]);

    $user = User::create([
        'unique_id' => $userID,
        'first_name' => $request->first_name,
        'last_name' => $request->last_name,
        'name' => $request->first_name." ".$request->last_name,
        'email' => $request->email,
        'password' => Hash::make("abcd1234"),
        'user_role' => 'customer',
        'status' => 'pending',
        'show_in_directory' => 'on',
    ]);

    event(new Registered($user));

    Auth::login($user);

    /* The following lines are where I am passing data to Stripe to create the subscription */

    $intent = $user->createSetupIntent();
    $clientSecret = $intent->client_secret;

    /* How do I pass $clientSecret to the subscription request from here??? */

    $subscription = $user->newSubscription($request->plan, $plan->stripe_plan)->create($request->token);

    return view("subscription_success");

As I am looking at the sample code, it seems to want to create the user first so that it can create an Intent, and then pass a client_secret string into a hidden field inside of the payment forms submit button.

So, I guess what I am ultimately asking is, how can I create that client_secret, pull it out of the Intent that was created, and pass that along to Stripe, so that it knows it’s kosher?

2

Answers


  1. You can get the payment_intent from the subscription’s latest_invoice, as explained in this integration doc

    Login or Signup to reply.
  2. There’re multiple ways to go about it

    Option 1A

    This is the preferred way, and Stripe has an example / guide for step 2 : https://stripe.com/docs/billing/subscriptions/build-subscriptions?ui=elements

    1. Create the Customer with no details first
    2. Create the Subscription and then retrieve the corresponding invoice’s PaymentIntent / SetupIntent and use the client_secret to render the Card / Payment Element. You would probably also want to set payment_settings.save_default_payment_method=true
    3. Update the Customer details

    Option 1B

    Unless you have a compelling reason to use option 1B, you should ideally use option 1A as it has less requests.

    1. Create the Customer with no details first
    2. Create the SetupIntent with the newly created customer from step 1
    3. Render the Card Element with the SetupIntent’s client_secret in step 2
    4. Update the Customer details
    5. Create the Subscription

    Option 2

    This option also uses more requests than 1A.

    1. Create a SetupIntent first (without creating and defining the customer) yet
    2. Render the page with the SetupIntent’s client_secret
    3. After the SetupIntent is successful, you can then create the Customer and attach the corresponding PaymentMethod to the Customer
    4. Create the Subscription

    Option 3

    I don’t recommend this option because it’s a pain to implement.

    1. Using the Card Element, you can create a PaymentMethod without a SetupIntent using stripe.createPaymentMethod.
    2. After the PaymentMethod is created/returned, you can then make a request to your server with the PaymentMethod id and other Customer details to create the Customer and Subscription.

    However, because the PaymentMethod was not created with a SetupIntent, when creating the Subscription, the issuer could require authentication and you would subsequently need to display the 3DS modal for the user to authenticate by calling stripe.confirmCardPayment with the Invoice’s PaymentIntent client_secret, and pass in the same Payment Method id

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search