stripe-ios icon indicating copy to clipboard operation
stripe-ios copied to clipboard

Ability to charge Subscription using the IOS Prebuilt UI.

Open worstkiller opened this issue 3 years ago • 6 comments

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.

worstkiller avatar Sep 02 '21 19:09 worstkiller

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!

davidme-stripe avatar Sep 07 '21 18:09 davidme-stripe

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

jmurphy-dev avatar Oct 07 '21 00:10 jmurphy-dev

@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

worstkiller avatar Oct 07 '21 17:10 worstkiller

@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()

jmurphy-dev avatar Oct 07 '21 17:10 jmurphy-dev

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

worstkiller avatar Oct 07 '21 18:10 worstkiller

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']
    });

edwardbeecroft avatar Apr 12 '22 15:04 edwardbeecroft