import SwiftUI
import SwiftData
import Stripe
@main
struct MySwiftUIApp: App {
var body: some Scene {
WindowGroup {
StripePaymentView()
//ContentView()
/*
ResponsiveView {properties in
MainTabbedView(layoutProperties: properties)
.modelContainer(for: SavedFavBooksFromISBNDB.self)
}
.onOpenURL { incomingURL in
let stripeHandled = StripeAPI.handleURLCallback(with: incomingURL)
if (!stripeHandled) {
// This was not a Stripe url – handle the URL normally as you would
}
}*/
}
}
}
代码有效,因为当我查看运行express.js脚本的后端服务器上的调试控制台时,我可以看到创建了正确金额的付款意图,如下所示
{paymentIntentID: 'pi_3PxEmnJ15oFjjfNB10B4Cm8o', amount: 7744}
但是,当我尝试从应用程序中必须放置视图“StripePaymentView”的任何其他子视图推送视图“StripePaymentView”时,在与后端服务器通信时出现错误。这是后端服务器上的调试控制台错误 下面是StripePaymentView的代码
import SwiftUI
import StripePaymentSheet
struct StripePaymentView: View {
@FocusState var textFieldFocused: Bool
@ObservedObject var model = StripePaymentHandler()
@State private var enteredNumber = ""
var enteredNumberFormatted: Double {
return (Double(enteredNumber) ?? 0) / 100
}
var body: some View {
VStack {
Text("Enter the amount")
ZStack(alignment: .center) {
Text("$\(enteredNumberFormatted, specifier: "%.2f")").font(Font.system(size: 30))
TextField("", text: $enteredNumber, onEditingChanged: { _ in
model.paymentAmount = Int(enteredNumberFormatted * 100)
}, onCommit: {
textFieldFocused = false
}).focused($textFieldFocused)
.keyboardType(.numberPad)
.foregroundColor(.clear)
.disableAutocorrection(true)
.accentColor(.clear)
}
Spacer()
if let paymentSheet = model.paymentSheet, !textFieldFocused {
PaymentSheet.PaymentButton(
paymentSheet: paymentSheet,
onCompletion: model.onPaymentCompletion
) {
payButton
}
}
}
.alert(model.alertText, isPresented: $model.showingAlert) {
Button("OK", role: .cancel) { }
}
.onChange(of: textFieldFocused) {
if !textFieldFocused {
DispatchQueue.global(qos: .background).sync {
model.updatePaymentSheet()
}
}
}
.onAppear {
model.preparePaymentSheet()
}
.padding(.horizontal)
.padding(.top, 50)
.padding(.bottom)
.toolbar {
ToolbarItem(placement: .keyboard) {
Button("Done") {
textFieldFocused = false
}
}
}
}
@ViewBuilder
var payButton: some View {
HStack {
Spacer()
Text("Pay $\(enteredNumberFormatted, specifier: "%.2f")")
Spacer()
}
.padding()
.foregroundColor(.white)
.background(
RoundedRectangle(cornerRadius: 10, style: .continuous)
.fill(.indigo)
)
}
}
以及与后端通信的 StripePaymentHandler 代码
import StripePaymentSheet
import SwiftUI
class StripePaymentHandler: ObservableObject {
@Published var paymentSheet: PaymentSheet?
@Published var showingAlert: Bool = false
private let backendtUrl = URL(string: "http://18.XXX.XX.XX:3000")!
private var configuration = PaymentSheet.Configuration()
private var clientSecret = ""
private var paymentIntentID: String = ""
var alertText: String = ""
var paymentAmount: Int = 0
func preparePaymentSheet() {
// MARK: Fetch the PaymentIntent and Customer information from the backend
let url = backendtUrl.appendingPathComponent("prepare-payment-sheet")
var request = URLRequest(url: url)
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 clientSecret = json["clientSecret"] as? String,
let paymentIntentID = json["paymentIntentID"] as? String,
let publishableKey = json["publishableKey"] as? String,
let self = self else {
// Handle error
return
}
self.clientSecret = clientSecret
self.paymentIntentID = paymentIntentID
STPAPIClient.shared.publishableKey = publishableKey
// MARK: Create a PaymentSheet instance
configuration.merchantDisplayName = "Example, Inc."
configuration.customer = .init(id: customerId, ephemeralKeySecret: customerEphemeralKeySecret)
configuration.allowsDelayedPaymentMethods = true
configuration.applePay = .init(
merchantId: "merchant.com.your_app_name",
merchantCountryCode: "US"
)
configuration.returnURL = "your-app://stripe-redirect"
})
task.resume()
}
func updatePaymentSheet() {
DispatchQueue.main.async {
self.paymentSheet = nil
}
let bodyProperties: [String: Any] = [
"paymentIntentID": paymentIntentID,
"amount": paymentAmount
]
let url = backendtUrl.appendingPathComponent("update-payment-sheet")
var request = URLRequest(url: url)
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = try? JSONSerialization.data(withJSONObject: bodyProperties)
request.httpMethod = "POST"
let task = URLSession.shared.dataTask(with: request, completionHandler: { [weak self] (data, response, error) in
guard let self = self else {
// Handle error
return
}
DispatchQueue.main.async {
self.paymentSheet = PaymentSheet(paymentIntentClientSecret: self.clientSecret, configuration: self.configuration)
}
})
task.resume()
}
func onPaymentCompletion(result: PaymentSheetResult) {
switch result {
case .completed:
self.alertText = "Payment complete!"
case .canceled:
self.alertText = "Payment canceled!"
case .failed(let error):
self.alertText = "Payment failed \(error.localizedDescription)"
}
showingAlert = true
}
}
这是在后端服务器上运行的express.js脚本
const stripe = require('stripe')('sk_test_XXXX');
const express = require('express');
const app = express();
app.use(express.json());
app.post('/prepare-payment-sheet', async (req, res) => {
const customer = await stripe.customers.create();
const ephemeralKey = await stripe.ephemeralKeys.create({customer: customer.id},
{apiVersion: '2024-04-10'});
const paymentIntent = await stripe.paymentIntents.create({
amount: 1099,
currency: 'usd',
customer: customer.id,
automatic_payment_methods: {
enabled: true,
},
});
res.json({
paymentIntentID: paymentIntent.id,
clientSecret: paymentIntent.client_secret,
ephemeralKey: ephemeralKey.secret,
customer: customer.id,
publishableKey: 'pk_test_XXXXX'
});
});
app.post('/update-payment-sheet', async (req, res) => {
const paymentIntent = await stripe.paymentIntents.update(
req.body.paymentIntentID,
{
amount: req.body.amount,
}
);
console.log(req.body)
console.log(res.body)
res.json({});
});
app.listen(3000, () => console.log('Running on port 3000'));