0
votes

I am using swiftUI. My code currently is using PassKit alone. It integrates with swiftUI nicely and I am able to get the payment.token encrypted from apple pay. I want to use that token, and tokenized it client-side (according to stripe docs, this is the best way to stay compliant with laws) and send it to stripe to complete the payment. I am having issue as to what API endpoints to call in order to send and complete all this while staying compliant. The docs only show using Stripe SDK which is very hard to integrate with swiftui.

import PassKit
import Stripe


typealias PaymentCompletionHandler = (Bool) -> Void

class PaymentHandler: NSObject{

static let supportedNetworks: [PKPaymentNetwork] = [
    .amex,
    .masterCard,
    .visa
]

var paymentController: PKPaymentAuthorizationController?
var paymentSummaryItems = [PKPaymentSummaryItem]()
var paymentStatus = PKPaymentAuthorizationStatus.failure
var completionHandler: PaymentCompletionHandler?

    func startPayment(client: ClientData, completion: @escaping PaymentCompletionHandler) {
        let cartTotal = client.cart.totalPrice
        let salesTax = client.cart.tax

        let amount = PKPaymentSummaryItem(label: "Amount", amount: NSDecimalNumber(value: cartTotal), type: .final)
        let tax = PKPaymentSummaryItem(label: "Tax", amount: NSDecimalNumber(value: salesTax), type: .final)
        let total = PKPaymentSummaryItem(label: "ToTal", amount: NSDecimalNumber(value: cartTotal + salesTax), type: .final)

    paymentSummaryItems = [amount, tax, total];
    completionHandler = completion

    // Create our payment request
    let paymentRequest = PKPaymentRequest()
    paymentRequest.paymentSummaryItems = paymentSummaryItems
    paymentRequest.merchantIdentifier = "merchant.com.xxxxx"
    paymentRequest.merchantCapabilities = .capability3DS
    paymentRequest.countryCode = "US"
    paymentRequest.currencyCode = "USD"
//    .phoneNumber, .emailAddress, .postalAddress
    paymentRequest.requiredShippingContactFields = []
    paymentRequest.supportedNetworks = PaymentHandler.supportedNetworks

    // Display our payment request
    paymentController = PKPaymentAuthorizationController(paymentRequest: paymentRequest)
    paymentController?.delegate = self
    paymentController?.present(completion: { (presented: Bool) in
        if presented {
            NSLog("Presented payment controller")
        } else {
            NSLog("Failed to present payment controller")
            self.completionHandler!(false)
         }
     })
  }
}

/*
    PKPaymentAuthorizationControllerDelegate conformance.
*/
extension PaymentHandler: PKPaymentAuthorizationControllerDelegate{
   
    

func paymentAuthorizationController(_ controller: PKPaymentAuthorizationController, didAuthorizePayment payment: PKPayment, completion: @escaping (PKPaymentAuthorizationStatus) -> Void) {

//    // Perform some very basic validation on the provided contact information
//    if payment.shippingContact?.emailAddress == nil || payment.shippingContact?.phoneNumber == nil {
//        paymentStatus = .failure
//    } else {
//        // Here you would send the payment token to your server or payment provider to process
//        // Once processed, return an appropriate status in the completion handler (success, failure, etc)
//        paymentStatus = .success
//    }
    let data = payment.token.paymentData
    let json = try! JSONSerialization.jsonObject(with: data, options: []) as? [String : Any]

    print(json)
    paymentStatus = .success
    completion(paymentStatus)
}

func paymentAuthorizationControllerDidFinish(_ controller: PKPaymentAuthorizationController) {
    controller.dismiss {
        DispatchQueue.main.async {
            if self.paymentStatus == .success {
                print()
                self.completionHandler!(true)
            } else {
                self.completionHandler!(false)
            }
        }
    }
}

}

I am thinking of sending the token/ tokenizing it in the paymentAuthorizationControllerDidFinish() function on .success and let stripe finish the charge.

Currently I think the steps are:

  1. Create a new stripe customer on backend when apple Pay is tapped -> send id back to client.

  2. Create a payment intent -> send id back to client.

3...? I am lost here on sending stripe all of the above along with the payment source, in my case is an encrypted payment token by apple.

i have tried sending a request to https://api.stripe.com/v1/charges? api and sending the entire token, but it says invalid request. I know its brute forcing but i am desperate :/

UPDATE 2

I used

    STPAPIClient.shared().createPaymentMethod(with: payment) { (source, err) in
        
        
        if let err = err {
            print(err.localizedDescription)
            self.paymentStatus = .failure
            completion(self.paymentStatus)
        }
        // returns a hashable [dictionary]
      
        guard let sourceDetails = source?.allResponseFields else
        {
            self.paymentStatus = .failure
            return
        }
        print(sourceDetails)
    

            AF.request("http://localhost:5001/rxxxxx/us-central1/paymentIntent", method: .post, parameters: ["data" : sourceDetails]).response { (response) in
                if let data = response.data {
                    guard let res = String(data: data, encoding: .utf8) else {return}
                    print(res)
                } else {
                    print("almo fire err")
                }


            }

And my backend returns a success response from Stripe [DELETED]


However, nothing has been charged or created in my Stripe dashboard.


**UPDATE 3**

I was able to confirm the payment intent using postman and calling the API and passing in the PayMent Method id directly. Still trying to implement using typescript.


LAST QUESTION. Is this PCI compliance? I have zero idea how all this is working. No client is created in dashboard, is this even working? The charges are being posted though. I am just confused because everything that i passed around didn't even show a full card number....
1

1 Answers

1
votes

If you aren't using the Stripe iOS SDK, try passing the PKPayment's properties to the /v1/tokens endpoint on Stripe in the parameters shown here: https://github.com/stripe/stripe-ios/blob/master/Stripe/STPAPIClient%2BApplePay.m#L121-L150 and

That gives you back a Token object. Create a PaymentMethod object using that Token object (as Tokens are legacy) with the /v1/payment_methods endpoint and passing card: {token: tok_visa }, which gets you back a PaymentMethod object: https://stripe.com/docs/api/payment_methods/create#create_payment_method-card

Now you can attach the PaymentMethod to a PaymentIntent and confirm it server-side.