Adyen / adyen-ios

Adyen iOS Drop-in and Components
https://docs.adyen.com/checkout/ios
MIT License
152 stars 122 forks source link

ApplePay token amount-mismatch #91

Closed brianpham93 closed 5 years ago

brianpham93 commented 5 years ago

This issue actually is not really due to adyen-ios library, however I think can find Adyen dev team's help here

I initiated an Apple Pay authorization using PassKit as instructed by Apple, with a transaction amount of 1.10SGD. However, whenever I sent this amount along with paymentData.token to Adyen's /payments endpoint, it will return

{
    "status": 422,
    "errorCode": "5_001",
    "message": "ApplePay token amount-mismatch",
    "errorType": "validation"
}

To investigate, I temporarily decrypted paymentData.token by reproducing Apple Pay authorization against my own Merchant ID, which I hold private key. Decryption is done by this library https://github.com/sidimansourjs/applepay-token. On decrypted data, I see that transaction amount is 110 SGD instead of 1.10 SGD as reflected on iPhone screen. Then, I updated my API request to payments endpoint to reflect transaction amount as 110 SGD instead and got a successful response

{
    "pspReference": "8825638XXXXXXX",
    "resultCode": "Authorised"
}

On Adyen's merchant dashboard, I could also see correct transaction amount 1.10 SGD instead of 110 SGD as in API request.

May I know how Adyen handle this case in backend and what's recommended for developer to handle transaction amount when send to Adyen's API ?

My sample code

class ViewController: UIViewController {
...
 @IBAction func payBtn(_ sender: UIButton) {
        do {
            let paymentItem = PKPaymentSummaryItem.init(label: "Test item", amount: NSDecimalNumber(value: 1.10))
            let paymentNetworks = [PKPaymentNetwork.amex, .discover, .masterCard, .visa]

            if PKPaymentAuthorizationViewController.canMakePayments(usingNetworks: paymentNetworks) {

                let request = PKPaymentRequest()
                request.currencyCode = "SGD" // 1
                request.countryCode = "SG" // 2
                request.merchantIdentifier = "merchant.com.xxxxx" // 3
                request.merchantCapabilities = PKMerchantCapability.capability3DS // 4
                request.supportedNetworks = paymentNetworks // 5
                request.paymentSummaryItems = [paymentItem] // 6

                guard let paymentVC = PKPaymentAuthorizationViewController(paymentRequest: request) else {
                    displayDefaultAlert(title: "Error", message: "Unable to present Apple Pay authorization.")
                    return
                }

                paymentVC.delegate = self
                self.present(paymentVC, animated: true, completion: nil)

            } else {
                displayDefaultAlert(title: "Error", message: "Unable to make Apple Pay transaction.")
            }
        } catch {
            print(error.localizedDescription)
        }

    }
}

extension ViewController: PKPaymentAuthorizationViewControllerDelegate {
    func paymentAuthorizationViewControllerDidFinish(_ controller: PKPaymentAuthorizationViewController) {
        dismiss(animated: true, completion: nil)
    }
    func paymentAuthorizationViewController(_ controller: PKPaymentAuthorizationViewController, didAuthorizePayment payment: PKPayment, handler completion: @escaping (PKPaymentAuthorizationResult) -> Void) {

        let token = String(data: payment.token.paymentData, encoding: .utf8)
        let utf8str = token!.data(using: .utf8)

        if let base64Encoded = utf8str?.base64EncodedString()
        {
            print("Encoded:  \(base64Encoded)")

            //Send token to backend server to decrypt
        }
    }
}
brianpham93 commented 5 years ago

I got it. Need to convert to minor unit, which is cent, 1/100 of a SGD. So need to multiply with 100 to get a cent value

gokulas1996 commented 3 years ago

Hi @brianpham93 Could you please specify where have you multiplied with 100. Did you multiply in the PKPaymentSummaryItem or in the amount specified in makePayment or in drop-in configuration

descorp commented 3 years ago

@gokulas1996

PKPaymentSummaryItem - takes NSDecimal as is. Drop-In expects "minor units": 1005 = "10 EUR 05 cents"

gokulas1996 commented 3 years ago

@descorp The issue was with the test card I was using. Thanks