1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
| // MARK: STPPaymentContextDelegate
extension CheckoutViewController: STPPaymentContextDelegate {
enum CheckoutError: Error {
case unknown
var localizedDescription: String {
switch self {
case .unknown:
return "Unknown error"
}
}
}
func paymentContext(_ paymentContext: STPPaymentContext, didCreatePaymentResult paymentResult: STPPaymentResult, completion: @escaping STPPaymentStatusBlock) {
// Create the PaymentIntent on the backend
// To speed this up, create the PaymentIntent earlier in the checkout flow and update it as necessary (e.g. when the cart subtotal updates or when shipping fees and taxes are calculated, instead of re-creating a PaymentIntent for every payment attempt.
MyAPIClient.sharedClient.createPaymentIntent(products: self.products, shippingMethod: paymentContext.selectedShippingMethod, country: self.country) { result in
switch result {
case .success(let clientSecret):
// Confirm the PaymentIntent
let paymentIntentParams = STPPaymentIntentParams(clientSecret: clientSecret)
paymentIntentParams.configure(with: paymentResult)
paymentIntentParams.returnURL = "payments-example://stripe-redirect"
STPPaymentHandler.shared().confirmPayment(withParams: paymentIntentParams, authenticationContext: paymentContext) { status, paymentIntent, error in
switch status {
case .succeeded:
// Our example backend asynchronously fulfills the customer's order via webhook
// See https://stripe.com/docs/payments/payment-intents/ios#fulfillment
completion(.success, nil)
case .failed:
completion(.error, error)
case .canceled:
completion(.userCancellation, nil)
@unknown default:
completion(.error, nil)
}
}
case .failure(let error):
// A real app should retry this request if it was a network error.
print("Failed to create a Payment Intent: \(error)")
completion(.error, error)
break
}
}
}
func paymentContext(_ paymentContext: STPPaymentContext, didFinishWith status: STPPaymentStatus, error: Error?) {
self.paymentInProgress = false
let title: String
let message: String
switch status {
case .error:
title = "Error"
message = error?.localizedDescription ?? ""
case .success:
title = "Success"
message = "Your purchase was successful!"
case .userCancellation:
return()
@unknown default:
return()
}
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
let action = UIAlertAction(title: "OK", style: .default, handler: nil)
alertController.addAction(action)
self.present(alertController, animated: true, completion: nil)
}
func paymentContextDidChange(_ paymentContext: STPPaymentContext) {
self.paymentRow.loading = paymentContext.loading
if let paymentOption = paymentContext.selectedPaymentOption {
self.paymentRow.detail = paymentOption.label
} else {
self.paymentRow.detail = "Select Payment"
}
if let shippingMethod = paymentContext.selectedShippingMethod {
self.shippingRow?.detail = shippingMethod.label
} else {
self.shippingRow?.detail = "Select address"
}
self.totalRow.detail = self.numberFormatter.string(from: NSNumber(value: Float(self.paymentContext.paymentAmount)/100))!
buyButton.isEnabled = paymentContext.selectedPaymentOption != nil && (paymentContext.selectedShippingMethod != nil || self.shippingRow == nil)
}
func paymentContext(_ paymentContext: STPPaymentContext, didFailToLoadWithError error: Error) {
let alertController = UIAlertController(
title: "Error",
message: error.localizedDescription,
preferredStyle: .alert
)
let cancel = UIAlertAction(title: "Cancel", style: .cancel, handler: { action in
// Need to assign to _ because optional binding loses @discardableResult value
// https://bugs.swift.org/browse/SR-1681
_ = self.navigationController?.popViewController(animated: true)
})
let retry = UIAlertAction(title: "Retry", style: .default, handler: { action in
self.paymentContext.retryLoading()
})
alertController.addAction(cancel)
alertController.addAction(retry)
self.present(alertController, animated: true, completion: nil)
}
// Note: this delegate method is optional. If you do not need to collect a
// shipping method from your user, you should not implement this method.
func paymentContext(_ paymentContext: STPPaymentContext, didUpdateShippingAddress address: STPAddress, completion: @escaping STPShippingMethodsCompletionBlock) {
let upsGround = PKShippingMethod()
upsGround.amount = 0
upsGround.label = "UPS Ground"
upsGround.detail = "Arrives in 3-5 days"
upsGround.identifier = "ups_ground"
let upsWorldwide = PKShippingMethod()
upsWorldwide.amount = 10.99
upsWorldwide.label = "UPS Worldwide Express"
upsWorldwide.detail = "Arrives in 1-3 days"
upsWorldwide.identifier = "ups_worldwide"
let fedEx = PKShippingMethod()
fedEx.amount = 5.99
fedEx.label = "FedEx"
fedEx.detail = "Arrives tomorrow"
fedEx.identifier = "fedex"
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
if address.country == nil || address.country == "US" {
completion(.valid, nil, [upsGround, fedEx], fedEx)
} else if address.country == "AQ" {
let error = NSError(domain: "ShippingError", code: 123, userInfo: [NSLocalizedDescriptionKey: "Invalid Shipping Address",
NSLocalizedFailureReasonErrorKey: "We can't ship to this country."])
completion(.invalid, error, nil, nil)
} else {
fedEx.amount = 20.99
fedEx.identifier = "fedex_world"
completion(.valid, nil, [upsWorldwide, fedEx], fedEx)
}
}
}
} |
Partager