下面是我的代码,主要是根据快速入门指南 (https://docs.stripe.com/ payments/quickstart) 以及 Stripe 文档的地址元素部分 (https://docs.stripe. com/elements/address-element/collect-addresses)。
import UIKit
import StripePaymentSheet
class CheckoutViewController: UIViewController {
private static let backendURL = URL(string: "https://sundealit.com/stripetest")!
private var paymentIntentClientSecret: String?
private var addressViewController: AddressViewController?
private var addressDetails: AddressViewController.AddressDetails?
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 // Initially disabled
return button
}()
private lazy var addressButton: UIButton = {
let button = UIButton(type: .custom)
button.setTitle("Add Address", 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(address), for: .touchUpInside)
button.translatesAutoresizingMaskIntoConstraints = false
button.isEnabled = true // Enable this button initially so user can add address
return button
}()
private var addressConfiguration: AddressViewController.Configuration {
return AddressViewController.Configuration(
additionalFields: .init(phone: .required),
allowedCountries: ["US", "CA", "GB", "HK"],
title: "Shipping Address"
)
}
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBackground
view.addSubview(payButton)
view.addSubview(addressButton)
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)
])
NSLayoutConstraint.activate([
addressButton.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 16),
addressButton.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -16),
addressButton.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, 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)
}
}
}
@objc
func address() {
self.addressViewController = AddressViewController(configuration: addressConfiguration, delegate: self)
let navigationController = UINavigationController(rootViewController: addressViewController!)
present(navigationController, animated: true)
}
}
extension CheckoutViewController: AddressViewControllerDelegate {
func addressViewControllerDidFinish(_ addressViewController: AddressViewController, with address: AddressViewController.AddressDetails?) {
addressViewController.dismiss(animated: true)
self.addressDetails = address
}
}
我的问题是,现在我可以从用户那里收集地址,“收集的地址”似乎存储在任何地方,并且在用户付款时不会被调用。如果对于每次付款我都无法指明我要将产品运送到哪里,那么这样的“收集地址”对我来说就没用。那么我如何修改代码以某种方式将两者链接在一起并能够在 Stripe 后端引用它? Stripe 文档似乎没有提供任何参考代码。
请帮忙...谢谢!
收集地址后,您可以将其作为 Stripe Doc 在此处提到预先填写到 PaymentSheet 配置的 ShippingDetails 中。
var configuration = PaymentSheet.Configuration()
// ...
configuration.shippingDetails = { [weak self] in
return self?.addressDetails
}
请注意,您还希望将配置移至
pay()
函数之外,并通过地址回调填充它,然后仅在 paymentSheet.present()
函数内调用 pay()
。