/* * Copyright (C) 2015-2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #import "config.h" #import "PaymentRequestValidator.h" #if ENABLE(APPLE_PAY) #import "ApplePaySessionPaymentRequest.h" #import "ApplePayShippingMethod.h" #import #import #import namespace WebCore { static ExceptionOr validateCountryCode(const String&); static ExceptionOr validateCurrencyCode(const String&); static ExceptionOr validateMerchantCapabilities(const ApplePaySessionPaymentRequest::MerchantCapabilities&); static ExceptionOr validateSupportedNetworks(const Vector&); static ExceptionOr validateShippingMethods(const Vector&); static ExceptionOr validateShippingMethod(const ApplePayShippingMethod&); ExceptionOr PaymentRequestValidator::validate(const ApplePaySessionPaymentRequest& paymentRequest, OptionSet fieldsToValidate) { if (fieldsToValidate.contains(Field::CountryCode)) { auto validatedCountryCode = validateCountryCode(paymentRequest.countryCode()); if (validatedCountryCode.hasException()) return validatedCountryCode.releaseException(); } if (fieldsToValidate.contains(Field::CurrencyCode)) { auto validatedCurrencyCode = validateCurrencyCode(paymentRequest.currencyCode()); if (validatedCurrencyCode.hasException()) return validatedCurrencyCode.releaseException(); } if (fieldsToValidate.contains(Field::SupportedNetworks)) { auto validatedSupportedNetworks = validateSupportedNetworks(paymentRequest.supportedNetworks()); if (validatedSupportedNetworks.hasException()) return validatedSupportedNetworks.releaseException(); } if (fieldsToValidate.contains(Field::MerchantCapabilities)) { auto validatedMerchantCapabilities = validateMerchantCapabilities(paymentRequest.merchantCapabilities()); if (validatedMerchantCapabilities.hasException()) return validatedMerchantCapabilities.releaseException(); } if (fieldsToValidate.contains(Field::Total)) { auto validatedTotal = validateTotal(paymentRequest.total()); if (validatedTotal.hasException()) return validatedTotal.releaseException(); } if (fieldsToValidate.contains(Field::ShippingMethods)) { auto validatedShippingMethods = validateShippingMethods(paymentRequest.shippingMethods()); if (validatedShippingMethods.hasException()) return validatedShippingMethods.releaseException(); } if (fieldsToValidate.contains(Field::CountryCode)) { for (auto& countryCode : paymentRequest.supportedCountries()) { auto validatedCountryCode = validateCountryCode(countryCode); if (validatedCountryCode.hasException()) return validatedCountryCode.releaseException(); } } return { }; } ExceptionOr PaymentRequestValidator::validateTotal(const ApplePayLineItem& total) { if (!total.label) return Exception { TypeError, "Missing total label."_s }; if (!total.amount) return Exception { TypeError, "Missing total amount."_s }; double amount = [NSDecimalNumber decimalNumberWithString:total.amount locale:@{ NSLocaleDecimalSeparator : @"." }].doubleValue; if (amount < 0) return Exception { TypeError, "Total amount must not be negative."_s }; if (amount > 100000000) return Exception { TypeError, "Total amount is too big."_s }; return { }; } static ExceptionOr validateCountryCode(const String& countryCode) { if (!countryCode) return Exception { TypeError, "Missing country code."_s }; for (auto *countryCodePtr = uloc_getISOCountries(); *countryCodePtr; ++countryCodePtr) { if (countryCode == StringView::fromLatin1(*countryCodePtr)) return { }; } return Exception { TypeError, makeString("\"" + countryCode, "\" is not a valid country code.") }; } static ExceptionOr validateCurrencyCode(const String& currencyCode) { if (!currencyCode) return Exception { TypeError, "Missing currency code."_s }; UErrorCode errorCode = U_ZERO_ERROR; auto currencyCodes = std::unique_ptr>(ucurr_openISOCurrencies(UCURR_ALL, &errorCode)); int32_t length; while (auto *currencyCodePtr = uenum_next(currencyCodes.get(), &length, &errorCode)) { if (currencyCode == StringView::fromLatin1(currencyCodePtr)) return { }; } return Exception { TypeError, makeString("\"" + currencyCode, "\" is not a valid currency code.") }; } static ExceptionOr validateMerchantCapabilities(const ApplePaySessionPaymentRequest::MerchantCapabilities& merchantCapabilities) { if (!merchantCapabilities.supports3DS && !merchantCapabilities.supportsEMV && !merchantCapabilities.supportsCredit && !merchantCapabilities.supportsDebit) return Exception { TypeError, "Missing merchant capabilities."_s }; return { }; } static ExceptionOr validateSupportedNetworks(const Vector& supportedNetworks) { if (supportedNetworks.isEmpty()) return Exception { TypeError, "Missing supported networks."_s }; return { }; } static ExceptionOr validateShippingMethod(const ApplePayShippingMethod& shippingMethod) { NSDecimalNumber *amount = [NSDecimalNumber decimalNumberWithString:shippingMethod.amount locale:@{ NSLocaleDecimalSeparator : @"." }]; if (amount.integerValue < 0) return Exception { TypeError, "Shipping method amount must be greater than or equal to zero."_s }; return { }; } static ExceptionOr validateShippingMethods(const Vector& shippingMethods) { for (const auto& shippingMethod : shippingMethods) { auto validatedShippingMethod = validateShippingMethod(shippingMethod); if (validatedShippingMethod.hasException()) return validatedShippingMethod.releaseException(); } return { }; } } #endif