#PaulSizer
1 messages · Page 1 of 1 (latest)
hey there
update their card in their settings
Can you explain exactly what you're trying to update?
Yeah sure, I want to update a users card details on their account. I have all the back end work already completed for the react-native app I have but I was just looking for a control for iOS similar to the (StripeProvider, CardField) on ReactNative
👋 I'm stepping in for my teammate. Could you let me know if this is what you're looking for? https://stripe.com/docs/payments/save-and-reuse-cards-only?platform=ios#ios-collect-card-details
Hey this looks promising, is this UIKit or does this work with SwiftUI
👋
Yep that guide is what you want. It is written for UIKit
The STPPaymentCardTextField is very similar to the React Native Card Field
Perfect, would you say this is the best way to handle what it is I am trying to achieve?
Is the StripePaymentSheet version more for checkouts? Rather than for saving card details?
Nope, you can use PaymentSheet to save cards as well. You just use a SetupIntent instead of a PaymentIntent.
I certainly recommend PaymentSheet as it gives you easy access to more payment method types
But it is mostly up to you and the UI that you are building
I tried with the PaymentSheet earlier but had a few issues with it. After clicking the button to display the payment sheet it would half come up drop down and then show an error. That was following the docs.
rror Domain=com.stripe.lib Code=50 "There was an unexpected error -- try again in a few seconds" UserInfo={com.stripe.lib:StripeErrorTypeKey=invalid_request_error, NSLocalizedDescription=There was an unexpected error -- try again in a few seconds, com.stripe.lib:StripeErrorCodeKey=resource_missing, com.stripe.lib:ErrorMessageKey=No such setupintent: 'seti_1M47RhGfAYgLoZtyKnwxIkzK', com.stripe.lib:ErrorParameterKey=intent}
Can you share your PaymentSheet code?
Yeah sure, should I just paste it in here?
import SwiftUI
import StripePaymentSheet
struct ClientCardDetailsView: View {
@ObservedObject var billingVM: ClientBillingViewModel
var body: some View {
ScrollView {
VStack(alignment: .center, spacing: 8) {
Text("Current Card Details")
.foregroundColor(.offWhite)
.font(.oswaldSubheadline)
if let billingDetails = billingVM.billingDetails {
Text("\(billingDetails.cardBrand.uppercased()) - Ending in \(billingDetails.cardLast4)")
.font(.latoCaption)
.foregroundColor(.lightGrey)
}
if let paymentSheet = billingVM.paymentSheet {
PaymentSheet.PaymentButton(
paymentSheet: paymentSheet,
onCompletion: billingVM.onPaymentCompletion
) {
Text("Update")
}
} else {
Text("Loading…")
}
if let result = billingVM.paymentResult {
switch result {
case .completed:
Text("Payment complete")
case .failed(let error):
Text("Payment failed: \(error.localizedDescription)")
case .canceled:
Text("Payment canceled.")
}
}
}
.padding()
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.navigationTitle("CARD DETAILS")
.navigationBarTitleDisplayMode(.inline)
.onAppear {
billingVM.preparePaymentSheet()
}
}
}
And your preparePaymentSheet() func?
func preparePaymentSheet() {
// MARK: Fetch the SetupIntent and Customer information from the backend
var request = URLRequest(url: backendCheckoutUrl)
request.setValue("Bearer \(token ?? "")", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
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 customerId = json["customer"] as? String,
let customerEphemeralKeySecret = json["ephemeralKey"] as? String,
let setupIntentClientSecret = json["setupIntent"] as? String,
let publishableKey = json["publishableKey"] as? String,
let self = self else {
// Handle error
return
}
STPAPIClient.shared.publishableKey = publishableKey
// MARK: Create a PaymentSheet instance
var configuration = PaymentSheet.Configuration()
configuration.returnURL = "your-app://stripe-redirect"
configuration.merchantDisplayName = "Example, Inc."
configuration.customer = .init(id: customerId, ephemeralKeySecret: customerEphemeralKeySecret)
configuration.allowsDelayedPaymentMethods = true
DispatchQueue.main.async {
self.paymentSheet = PaymentSheet(setupIntentClientSecret: setupIntentClientSecret, configuration: configuration)
}
})
task.resume()
}
func onPaymentCompletion(result: PaymentSheetResult) {
self.paymentResult = result
}
Server
def payment_sheet @client = session_user.client @account_id = @client.coach.organization.stripe_connect_id @setup_intent = CreatePaymentSheetSetupIntent.new({account_id: @account_id, customer_id: @client.stripe_id}).call if @setup_intent[:success] render json: { setupIntent: @setup_intent[:setupIntent], ephemeralKey: @setup_intent[:ephemeralKey], customer: @setup_intent[:customer], publishableKey: @setup_intent[:publishableKey] }, status: 201 else render json: { error: @setup_intent[:error] }, status: 422 end end
Ah this is Connect
require 'stripe'class CreatePaymentSheetSetupIntent def initialize(params) @account_id = params[:account_id] @customer_id = params[:customer_id] end def call begin # This will return a Stripe::Charge object if !@customer_id customer = Stripe::Customer.create @customer_id = customer['id'] end ephemeralKey = Stripe::EphemeralKey.create({ customer: @customer_id, },{stripe_account: @account_id, stripe_version: '2022-08-01'}) # Stripe.api_version = '2022-08-01;automatic_payment_methods_beta=v1' setupIntent = Stripe::SetupIntent.create({ customer: @customer_id }, {stripe_account: @account_id}) return { setupIntent: setupIntent['client_secret'], ephemeralKey: ephemeralKey['secret'], customer: @customer_id, publishableKey: 'XXX', success: true } rescue Stripe::StripeError => e # Since it's a decline, Stripe::CardError will be caught puts "Status is: #{e.http_status}" puts "Type is: #{e.error.type}" puts "Code is: #{e.error.code}" {success: false, error: e.error.message } end end private def external_setup_intent_service Stripe::SetupIntent end attr_reader :account_id, :descriptionend
Are you initializing the Stripe API Client with the Connected Account like so: https://stripe.com/docs/connect/authentication#adding-the-connected-account-id-to-a-client-side-application ?
Perfect thank you
That fix it?
I added the following
STPAPIClient.shared.stripeAccount = accountId
under where I set the publishable key bit that didn't fix it
I think I see whats up, its the account now that was created with a test account and now I'm using the live key.
Ah yep that would be an issue
👍
🎉
How do I pass the payment method to my backend now?
The recommended route is to use Webhooks
You would listen for setup_intent.succeeded
Otherwise you can fetch your backend and list PaymentMethods for that Customer: https://stripe.com/docs/api/payment_methods/customer_list
Or even pass the SetupIntent back to your backend and retrieve the SetupIntent
Variety of ways, really.
Smashing thank you