1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "components/payments/core/payment_details_validation.h"
6 
7 #include <set>
8 #include <vector>
9 
10 #include "components/payments/core/payment_details.h"
11 #include "components/payments/core/payments_validators.h"
12 
13 namespace payments {
14 namespace {
15 
16 // Validates ShippingOption or PaymentItem, which happen to have identical
17 // fields, except for "id", which is present only in ShippingOption.
18 template <typename T>
ValidateShippingOptionOrPaymentItem(const T & item,const PaymentItem & total,std::string * error_message)19 bool ValidateShippingOptionOrPaymentItem(const T& item,
20                                          const PaymentItem& total,
21                                          std::string* error_message) {
22   if (!item.amount) {
23     *error_message = "Amount required";
24     return false;
25   }
26 
27   if (item.amount->currency.empty()) {
28     *error_message = "Currency code required";
29     return false;
30   }
31 
32   if (item.amount->value.empty()) {
33     *error_message = "Currency value required";
34     return false;
35   }
36 
37   if (!payments::PaymentsValidators::IsValidCurrencyCodeFormat(
38           item.amount->currency, error_message)) {
39     return false;
40   }
41 
42   if (!payments::PaymentsValidators::IsValidAmountFormat(item.amount->value,
43                                                          error_message)) {
44     return false;
45   }
46   return true;
47 }
48 
ValidateDisplayItems(const std::vector<PaymentItem> & items,const PaymentItem & total,std::string * error_message)49 bool ValidateDisplayItems(const std::vector<PaymentItem>& items,
50                           const PaymentItem& total,
51                           std::string* error_message) {
52   for (const auto& item : items) {
53     if (!ValidateShippingOptionOrPaymentItem(item, total, error_message))
54       return false;
55   }
56   return true;
57 }
58 
ValidateShippingOptions(const std::vector<PaymentShippingOption> & options,const PaymentItem & total,std::string * error_message)59 bool ValidateShippingOptions(const std::vector<PaymentShippingOption>& options,
60                              const PaymentItem& total,
61                              std::string* error_message) {
62   std::set<std::string> uniqueIds;
63   for (const auto& option : options) {
64     if (option.id.empty()) {
65       *error_message = "ShippingOption id required";
66       return false;
67     }
68 
69     if (uniqueIds.find(option.id) != uniqueIds.end()) {
70       *error_message = "Duplicate shipping option identifiers are not allowed";
71       return false;
72     }
73     uniqueIds.insert(option.id);
74 
75     if (!ValidateShippingOptionOrPaymentItem(option, total, error_message))
76       return false;
77   }
78   return true;
79 }
80 
ValidatePaymentDetailsModifiers(const std::vector<PaymentDetailsModifier> & modifiers,const PaymentItem & total,std::string * error_message)81 bool ValidatePaymentDetailsModifiers(
82     const std::vector<PaymentDetailsModifier>& modifiers,
83     const PaymentItem& total,
84     std::string* error_message) {
85   if (modifiers.empty()) {
86     *error_message = "Must specify at least one payment details modifier";
87     return false;
88   }
89 
90   for (const auto& modifier : modifiers) {
91     if (modifier.method_data.supported_method.empty()) {
92       *error_message = "Must specify payment method identifier";
93       return false;
94     }
95 
96     if (modifier.total) {
97       if (!ValidateShippingOptionOrPaymentItem(*modifier.total, total,
98                                                error_message))
99         return false;
100 
101       if (modifier.total->amount->value[0] == '-') {
102         *error_message = "Total amount value should be non-negative";
103         return false;
104       }
105     }
106 
107     if (modifier.additional_display_items.size()) {
108       if (!ValidateDisplayItems(modifier.additional_display_items, total,
109                                 error_message)) {
110         return false;
111       }
112     }
113   }
114   return true;
115 }
116 
117 }  // namespace
118 
ValidatePaymentDetails(const PaymentDetails & details,std::string * error_message)119 bool ValidatePaymentDetails(const PaymentDetails& details,
120                             std::string* error_message) {
121   if (details.total) {
122     if (!ValidateShippingOptionOrPaymentItem(*details.total, *details.total,
123                                              error_message)) {
124       return false;
125     }
126 
127     if (details.total->amount->value[0] == '-') {
128       *error_message = "Total amount value should be non-negative";
129       return false;
130     }
131   }
132 
133   if (details.display_items.size()) {
134     if (!ValidateDisplayItems(details.display_items, *details.total,
135                               error_message))
136       return false;
137   }
138 
139   if (details.shipping_options.size()) {
140     if (!ValidateShippingOptions(details.shipping_options, *details.total,
141                                  error_message))
142       return false;
143   }
144 
145   if (details.modifiers.size()) {
146     if (!ValidatePaymentDetailsModifiers(details.modifiers, *details.total,
147                                          error_message))
148       return false;
149   }
150   if (!PaymentsValidators::IsValidErrorMsgFormat(details.error, error_message))
151     return false;
152   return true;
153 }
154 
155 }  // namespace payments
156