stripe / stripe-ios

Stripe iOS SDK
https://stripe.com
MIT License
2.13k stars 985 forks source link

Ability to charge Subscription using the IOS Prebuilt UI. #1868

Closed worstkiller closed 4 months ago

worstkiller commented 3 years ago

Summary

This is to bring to notice that I'm somehow not able to configure and use the Stripe IOS SDK with the subscriptions. I am able to integrate it with the one-time payment and docs as provided but there is no way i can charge the user based on subscription. If this is available please point me to Native iOS implementation for doing this, otherwise please consider this as a feature request

Code to reproduce

Follow the iOS docs for payment integration.

iOS version

14 and above

Installation method

cocoapods

SDK version

21.8.1

Other information

I have done the backend deployment part but not sure how to make it work at the frontend native. One more thing i would like to point it out is that i am using Swift UI implementation.

davidme-stripe commented 3 years ago

Hello! You should be able to do this by creating a Subscription with payment_behavior: 'default_incomplete', which will give you a PaymentIntent client secret. You'll want to pass that client secret to PaymentSheet, then follow the rest of the PaymentSheet integration guide as usual. If you need help with this, our support team can walk you through it.

I agree that we need an example to explain this, so I'll leave this issue open to track building one!

jmurphy-dev commented 3 years ago

@worstkiller did you ever get this to work? im running into the same issue.

worstkiller commented 3 years ago

@worstkiller did you ever get this to work? im running into the same issue.

Hi @summer-mute for the moment i went ahead with webview way. Opening a Stripe checkout payment link directly and listening for success or failure redirection.

if anything comes up will let you know

Thanks Vikas Kumar

jmurphy-dev commented 3 years ago

@worstkiller

this worked for me

sever -

app.post("/create-payment-intent", async (req, res) => {
    const customer = await stripe.customers.create();
    const ephemeralKey = await stripe.ephemeralKeys.create(
        {customer: customer.id},
        {apiVersion: '2020-08-27'}
    )
    const subscription = await stripe.subscriptions.create({
          customer: customer.id,
          items: [
            {price: 'price_1JfB2RKdI6ztUgqfnckNNSge'},
          ],
          payment_behavior: 'default_incomplete',
          expand: ['latest_invoice.payment_intent'],
    });

    res.json({
        paymentIntent: subscription.latest_invoice.payment_intent.client_secret,
        ephemeralKey: ephemeralKey.secret,
        customer: customer.id
    });
});

client -

let task = URLSession.shared.dataTask(with: request, completionHandler: { [weak self] (data, response, error) in

          guard let data = data,
                let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String : Any],
                let paymentIntentClientSecret = json["paymentIntent"] as? String,
                let customerEphemeralKeySecret = json["ephemeralKey"] as? String,
                let customerId = json["customer"] as? String,
                let self = self else {
                    print("Payment Error")
                    return
                }

          // MARK: Create a PaymentSheet instance
          var configuration = PaymentSheet.Configuration()
          configuration.customer = .init(id: customerId, ephemeralKeySecret: customerEphemeralKeySecret)

          DispatchQueue.main.async {
            self.paymentSheet = PaymentSheet(paymentIntentClientSecret: paymentIntentClientSecret, configuration: configuration)
          }
        })
        task.resume()
worstkiller commented 3 years ago

okay got it that should be the way to do it. Thanks for sharing it 👏.

edwardbeecroft commented 2 years ago

Hello! You should be able to do this by creating a Subscription with payment_behavior: 'default_incomplete', which will give you a PaymentIntent client secret. You'll want to pass that client secret to PaymentSheet, then follow the rest of the PaymentSheet integration guide as usual. If you need help with this, our support team can walk you through it.

I agree that we need an example to explain this, so I'll leave this issue open to track building one!

Sorry to bump this thread - but I thought it a) was quite an appropriate place given it might help others, and b) gives increased visibility to the need for this. Feel free to tell me to create a support ticket, though!

This worked for me, until I needed to set a custom start date for the subscription. If you set billing_cycle_anchor and proration_behavior, no payment intent is received (because latest_invoice is null).

I then noticed that I could expand pending_setup_intent to get a client_secret from SetupIntent, which I could then pass to the PaymentSheet.

This works perfectly - however for some reason every subscription is created with a status of active. This means that if a user visits your checkout screen, taps "Pay", but then doesn't complete - you end up with a bunch of active subscriptions with no payment method. If, at a later date, the user completes, they'd end up with many active subscriptions.

The question therefore, is why does a subscription start with a status of active when a billing_cycle_anchor and proration_behavior are provided - even when payment_behavior is set to default_incomplete?

The solution to all of this looks like Subscription Schedules, but are they compatible with the iOS SDK?

Sample code for subscription creation:

const subscription = await stripe.subscriptions.create({
      customer: customer.id,
      items: items,
      metadata: {
        booking_id: request.body.bookingID,
        user_id: request.body.userID
      },
      payment_behavior: 'default_incomplete',
      billing_cycle_anchor: request.body.startDateTimestamp,
      proration_behavior: 'none',
      expand: ['pending_setup_intent']
    });
sfriedman-stripe commented 4 months ago

Hi all, we now have a full guide on this here. Please let us know if you have any feedback by opening a new issue. Thank you!