如何在Stripe PaymentSheet中添加收货地址收集功能?
解决Stripe PaymentSheet收货地址收集问题
我基于Stripe提供的基础模板编写了Swift代码,但该模板无法让用户输入收货地址,导致付款后无法获取商品配送地址。查阅官方文档后对步骤4的代码插入位置感到困惑,现有代码如下:
import UIKit import StripePaymentSheet class CheckoutViewController: UIViewController { private static let backendURL = URL(string: "https://sundealit.com/stripetest")! private var paymentIntentClientSecret: String? private lazy var payButton: UIButton = { let button = UIButton(type: .custom) button.setTitle("Pay now", for: .normal) button.backgroundColor = .systemIndigo button.layer.cornerRadius = 5 button.contentEdgeInsets = UIEdgeInsets(top: 12, left: 12, bottom: 12, right: 12) button.addTarget(self, action: #selector(pay), for: .touchUpInside) button.translatesAutoresizingMaskIntoConstraints = false button.isEnabled = false return button }() override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .systemBackground view.addSubview(payButton) NSLayoutConstraint.activate([ payButton.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 16), payButton.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -16), payButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -16) ]) self.fetchPaymentIntent() } func fetchPaymentIntent() { let url = Self.backendURL.appendingPathComponent("/create-payment-intent") let shoppingCartContent: [String: Any] = [ "items": [ ["id": "xl-shirt"] ] ] var request = URLRequest(url: url) request.httpMethod = "POST" request.setValue("application/json", forHTTPHeaderField: "Content-Type") request.httpBody = try? JSONSerialization.data(withJSONObject: shoppingCartContent) let task = URLSession.shared.dataTask(with: request, completionHandler: { [weak self] (data, response, error) in guard let response = response as? HTTPURLResponse, response.statusCode == 200, let data = data, let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String : Any], let clientSecret = json["clientSecret"] as? String, let publishableKey = json["publishableKey"] as? String else { let message = error?.localizedDescription ?? "Failed to decode response from server." self?.displayAlert(title: "Error loading page", message: message) return } print("Created PaymentIntent") print(clientSecret) print(publishableKey) self?.paymentIntentClientSecret = clientSecret StripeAPI.defaultPublishableKey = publishableKey DispatchQueue.main.async { self?.payButton.isEnabled = true } }) task.resume() } func displayAlert(title: String, message: String? = nil) { DispatchQueue.main.async { let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert) alertController.addAction(UIAlertAction(title: "OK", style: .default)) self.present(alertController, animated: true) } } @objc func pay() { guard let paymentIntentClientSecret = self.paymentIntentClientSecret else { return } var configuration = PaymentSheet.Configuration() configuration.merchantDisplayName = "Example, Inc." let paymentSheet = PaymentSheet( paymentIntentClientSecret: paymentIntentClientSecret, configuration: configuration) paymentSheet.present(from: self) { [weak self] (paymentResult) in switch paymentResult { case .completed: self?.displayAlert(title: "Payment complete!") case .canceled: print("Payment canceled!") case .failed(let error): self?.displayAlert(title: "Payment failed", message: error.localizedDescription) } } } }
修改方案
要实现收货地址收集,只需在pay()方法中修改PaymentSheet.Configuration的配置,添加收货地址相关设置,并配置回调获取用户输入的地址。
1. 配置收货地址收集规则
在创建configuration后,添加以下代码:
// 设置收货类型为配送 configuration.shippingType = .shipping // 配置允许的收货国家(按需修改) configuration.shippingAddressCollection = .init(countries: ["US", "CA", "CN"])
2. 添加地址回调获取用户输入的地址
首先让CheckoutViewController遵循PaymentSheetShippingDetailsDelegate协议:
class CheckoutViewController: UIViewController, PaymentSheetShippingDetailsDelegate {
然后在pay()方法中绑定代理:
// 绑定配送地址代理 paymentSheet.shippingDetailsDelegate = self
最后实现协议方法,获取并处理用户输入的地址:
// 当用户提交收货地址时触发 func paymentSheet(_ paymentSheet: PaymentSheet, didUpdateShippingDetails shippingDetails: PaymentSheet.ShippingDetails, completion: @escaping (PaymentSheet.ShippingDetailsUpdateResult) -> Void) { // 可在此添加地址验证、运费计算等逻辑 // 直接确认地址有效,继续支付流程 completion(.success(.init(shippingOptions: []))) // 保存用户输入的地址信息(按需存储到本地或后端) print("用户收货地址:") print("姓名:\(shippingDetails.name)") print("地址行1:\(shippingDetails.address.line1)") if let line2 = shippingDetails.address.line2 { print("地址行2:\(line2)") } print("城市:\(shippingDetails.address.city)") if let state = shippingDetails.address.state { print("州/省:\(state)") } print("邮编:\(shippingDetails.address.postalCode)") print("国家:\(shippingDetails.address.country)") if let phone = shippingDetails.phone { print("电话:\(phone)") } }
3. 后端配合(重要)
确保后端在创建PaymentIntent时,允许收集收货地址信息。支付完成后,你可以通过Stripe Dashboard查看该PaymentIntent的收货地址,或通过后端Webhook获取地址详情并存储到自身数据库。
修改后的完整代码
import UIKit import StripePaymentSheet class CheckoutViewController: UIViewController, PaymentSheetShippingDetailsDelegate { private static let backendURL = URL(string: "https://sundealit.com/stripetest")! private var paymentIntentClientSecret: String? private lazy var payButton: UIButton = { let button = UIButton(type: .custom) button.setTitle("Pay now", for: .normal) button.backgroundColor = .systemIndigo button.layer.cornerRadius = 5 button.contentEdgeInsets = UIEdgeInsets(top: 12, left: 12, bottom: 12, right: 12) button.addTarget(self, action: #selector(pay), for: .touchUpInside) button.translatesAutoresizingMaskIntoConstraints = false button.isEnabled = false return button }() override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .systemBackground view.addSubview(payButton) NSLayoutConstraint.activate([ payButton.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 16), payButton.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -16), payButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -16) ]) self.fetchPaymentIntent() } func fetchPaymentIntent() { let url = Self.backendURL.appendingPathComponent("/create-payment-intent") let shoppingCartContent: [String: Any] = [ "items": [ ["id": "xl-shirt"] ] ] var request = URLRequest(url: url) request.httpMethod = "POST" request.setValue("application/json", forHTTPHeaderField: "Content-Type") request.httpBody = try? JSONSerialization.data(withJSONObject: shoppingCartContent) let task = URLSession.shared.dataTask(with: request, completionHandler: { [weak self] (data, response, error) in guard let response = response as? HTTPURLResponse, response.statusCode == 200, let data = data, let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String : Any], let clientSecret = json["clientSecret"] as? String, let publishableKey = json["publishableKey"] as? String else { let message = error?.localizedDescription ?? "Failed to decode response from server." self?.displayAlert(title: "Error loading page", message: message) return } print("Created PaymentIntent") print(clientSecret) print(publishableKey) self?.paymentIntentClientSecret = clientSecret StripeAPI.defaultPublishableKey = publishableKey DispatchQueue.main.async { self?.payButton.isEnabled = true } }) task.resume() } func displayAlert(title: String, message: String? = nil) { DispatchQueue.main.async { let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert) alertController.addAction(UIAlertAction(title: "OK", style: .default)) self.present(alertController, animated: true) } } @objc func pay() { guard let paymentIntentClientSecret = self.paymentIntentClientSecret else { return } var configuration = PaymentSheet.Configuration() configuration.merchantDisplayName = "Example, Inc." // 新增:配置收货地址收集 configuration.shippingType = .shipping configuration.shippingAddressCollection = .init(countries: ["US", "CA", "CN"]) let paymentSheet = PaymentSheet( paymentIntentClientSecret: paymentIntentClientSecret, configuration: configuration) // 新增:绑定配送地址代理 paymentSheet.shippingDetailsDelegate = self paymentSheet.present(from: self) { [weak self] (paymentResult) in switch paymentResult { case .completed: self?.displayAlert(title: "Payment complete!") case .canceled: print("Payment canceled!") case .failed(let error): self?.displayAlert(title: "Payment failed", message: error.localizedDescription) } } } // 新增:实现配送地址代理方法 func paymentSheet(_ paymentSheet: PaymentSheet, didUpdateShippingDetails shippingDetails: PaymentSheet.ShippingDetails, completion: @escaping (PaymentSheet.ShippingDetailsUpdateResult) -> Void) { // 验证地址或计算运费逻辑可在此添加 completion(.success(.init(shippingOptions: []))) // 打印并保存用户地址信息 print("用户收货地址:") print("姓名:\(shippingDetails.name)") print("地址行1:\(shippingDetails.address.line1)") if let line2 = shippingDetails.address.line2 { print("地址行2:\(line2)") } print("城市:\(shippingDetails.address.city)") if let state = shippingDetails.address.state { print("州/省:\(state)") } print("邮编:\(shippingDetails.address.postalCode)") print("国家:\(shippingDetails.address.country)") if let phone = shippingDetails.phone { print("电话:\(phone)") } } }
内容的提问来源于stack exchange,提问作者Bosco Tsin
相关产品推荐
相关产品推荐

