The goal is to just make a successful Apple Pay payment and be done with this API integration. I'm literally on the brink of success but I have just one more bug I can't figure out.
func startApplePayCheckout() {
let backendUrlForIntent = "https://us-central1-xxxxx-41f12.cloudfunctions.net/createPaymentIntent"
guard let user = Auth.auth().currentUser else { return }
getUsersStripeCustomerID { (customerid) in
if let id = customerid {
// Create a PaymentIntent as soon as the view loads
let costAsAnInt = self.actualCostOfEvent.text?.replacingOccurrences(of: "$", with: "").replacingOccurrences(of: ".", with: "")
let costForStripe = Int(costAsAnInt!)
let url = URL(string: backendUrlForIntent)!
let json: [String: Any] = [
"amount": costForStripe! + (self.gothereFee * Int(self.stepperValue.value)),
"currency": "CAD",
"customer": id,
"setup_future_usage": "on_session",
]
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = try? JSONSerialization.data(withJSONObject: json)
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 else {
let message = error?.localizedDescription ?? "Failed to decode response from server."
self?.displayCancelledAlert(title: "Error Loading Page", message: message)
return
}
guard let paymentIntentID = json["paymentIntentID"] as? String else { return }
self?.db.document("student_users/\(user.uid)/events_bought/\(self?.nameToUseInAPICall)").setData(["stripePaymentIntentID": paymentIntentID], merge: true, completion: { (error) in
if let error = error {
print("There was an error setting the paymentIntentID in the document: \(error)")
} else {
print("PaymentIntentID successfully stored!")
}
})
print("Created PaymentIntent")
self?.paymentIntentClientSecret = clientSecret
})
task.resume()
}
}
I call this right when the Apple Pay sheet is presented. The print statements work fine, the clientSecret gets retrieved. Now when I actually try to pay, I still get a "Payment Not Completed", this is what I have in the didCreatePaymentMethod
function:
func applePayContext(_ context: STPApplePayContext, didCreatePaymentMethod paymentMethod: STPPaymentMethod, paymentInformation: PKPayment, completion: @escaping STPIntentClientSecretCompletionBlock) {
guard let paymentIntentClient = paymentIntentClientSecret else {
print("There is an issue with the clientSecret")
return
}
let error = NSError(domain: backendUrl, code: 400, userInfo: [NSLocalizedDescriptionKey: "The payment cannot go through for some reason!"])
let paymentIntentParams = STPPaymentIntentParams(clientSecret: paymentIntentClient)
paymentIntentParams.paymentMethodId = paymentMethod.stripeId
let paymentHandler = STPPaymentHandler.shared()
paymentHandler.confirmPayment(paymentIntentParams, with: self) { (status, intent, error) in
switch status {
case .canceled:
print("Payment Canceled")
break
case .failed:
print("Payment Failed")
break
case .succeeded:
print("Payment Succeeded")
break
default:
break
}
}
completion(paymentIntentClient, error)
}
EDIT
So with this implemented, the payment actually charges now when I check in the logs, but for the actual Apple Pay itself, it never shows the success message in the Apple Pay sheet. Here is what I have in the didCompleteWithStatus
method:
func applePayContext(_ context: STPApplePayContext, didCompleteWith status: STPPaymentStatus, error: Error?) {
guard status == .success else {
print("Payment couldn't go through")
return
}
}
So the weird thing that's going on is that, the first method ends up being successful and the charge is shown in the API Call logs, but the didCompleteWith...
method always finishes with an error status and a "Payment Not Completed" on the Apple sheet and for the life of me I cannot understand why.
I believe the issue is that you're creating an error on this line:
let error = NSError(domain: backendUrl, code: 400, userInfo: [NSLocalizedDescriptionKey: "The payment cannot go through for some reason!"])
And then feeding that error into the completion
handler here:
completion(paymentIntentClient, error)
That results in the "Payment not complete" error being shown in the Apple Pay sheet.
The idea with the completion
handler is to provide it a Payment Intent's client secret or an error if you can't provide it with a client secret, not both. The documentation explains this:
Call this with the PaymentIntent’s client secret, or the error that occurred creating the PaymentIntent.
If you call completion
without the error
you should be all set.