SwissQRCode.cs 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543
  1. using FastReport.Utils;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Text.RegularExpressions;
  6. namespace FastReport.Barcode
  7. {
  8. /// <summary>
  9. /// Represents a class that contains all parameters of Swiss QR Code.
  10. /// </summary>
  11. public class QRSwissParameters
  12. {
  13. #region private fields
  14. private Iban iban;
  15. private string currency;
  16. private Contact creditor;
  17. private Reference reference;
  18. private AdditionalInformation additionalInformation;
  19. private Contact debitor;
  20. private string amount;
  21. private string alternativeProcedure1;
  22. private string alternativeProcedure2;
  23. #endregion
  24. //Pattern extracted from https://qr-validation.iso-payments.ch as explained in https://github.com/codebude/QRCoder/issues/97
  25. internal static string charsetPattern = @"^([a-zA-Z0-9\.,;:'\ \+\-/\(\)?\*\[\]\{\}\\`´~ ]|[!""#%&<>÷=@_$£]|[àáâäçèéêëìíîïñòóôöùúûüýßÀÁÂÄÇÈÉÊËÌÍÎÏÒÓÔÖÙÚÛÜÑ])*$";
  26. #region public properties
  27. /// <summary>
  28. /// IBAN object
  29. /// </summary>
  30. public Iban Iban { get { return iban; } set { iban = value; } }
  31. /// <summary>
  32. /// (either EUR or CHF)
  33. /// </summary>
  34. public string Currency { get { return currency; } set { currency = value; } }
  35. /// <summary>
  36. /// Creditor (payee) information
  37. /// </summary>
  38. public Contact Creditor { get { return creditor; } set { creditor = value; } }
  39. /// <summary>
  40. /// Reference information
  41. /// </summary>
  42. public Reference Reference { get { return reference; } set { reference = value; } }
  43. /// <summary>
  44. /// Can be null
  45. /// </summary>
  46. public AdditionalInformation AdditionalInformation { get { return additionalInformation; } set { additionalInformation = value; } }
  47. /// <summary>
  48. /// Debitor (payer) information
  49. /// </summary>
  50. public Contact Debitor { get { return debitor; } set { debitor = value; } }
  51. /// <summary>
  52. /// Amount
  53. /// </summary>
  54. public string Amount { get { return amount; } set { amount = value; } }
  55. /// <summary>
  56. /// Optional command for alternative processing mode - line 1
  57. /// </summary>
  58. public string AlternativeProcedure1 { get { return alternativeProcedure1; } set { alternativeProcedure1 = value; } }
  59. /// <summary>
  60. /// Optional command for alternative processing mode - line 2
  61. /// </summary>
  62. public string AlternativeProcedure2 { get { return alternativeProcedure2; } set { alternativeProcedure2 = value; } }
  63. #endregion
  64. }
  65. public class AdditionalInformation
  66. {
  67. private string unstructuredMessage, billInformation, trailer;
  68. /// <summary>
  69. /// Creates an additional information object. Both parameters are optional and must be shorter than 141 chars in combination.
  70. /// </summary>
  71. /// <param name="unstructuredMessage">Unstructured text message</param>
  72. /// <param name="billInformation">Bill information</param>
  73. public AdditionalInformation(string unstructuredMessage, string billInformation)
  74. {
  75. MyRes res = new MyRes("Messages,Swiss");
  76. if (((unstructuredMessage != null ? unstructuredMessage.Length : 0) + (billInformation != null ? billInformation.Length : 0)) > 140)
  77. throw new SwissQrCodeException(res.Get("SwissUnstructBillLength"));
  78. if (!Regex.IsMatch(unstructuredMessage, QRSwissParameters.charsetPattern))
  79. throw new SwissQrCodeException(String.Format(res.Get("SwissPatternError"), res.Get("SwissPropUnstructuredMessage")) + QRSwissParameters.charsetPattern);
  80. if (!Regex.IsMatch(billInformation, QRSwissParameters.charsetPattern))
  81. throw new SwissQrCodeException(String.Format(res.Get("SwissPatternError"), res.Get("SwissPropBillInformation")) + QRSwissParameters.charsetPattern);
  82. this.unstructuredMessage = unstructuredMessage;
  83. this.billInformation = billInformation;
  84. this.trailer = "EPD";
  85. }
  86. public AdditionalInformation(string addInfo)
  87. {
  88. string[] data = addInfo.Split('\r');
  89. this.trailer = data[1].Trim();
  90. this.unstructuredMessage = data[0].Trim();
  91. this.billInformation = data[2].Trim();
  92. }
  93. public string UnstructureMessage
  94. {
  95. get { return !string.IsNullOrEmpty(unstructuredMessage) ? unstructuredMessage.Replace("\n", "") : null; }
  96. set { this.unstructuredMessage = value; }
  97. }
  98. public string BillInformation
  99. {
  100. get { return !string.IsNullOrEmpty(billInformation) ? billInformation.Replace("\n", "") : null; }
  101. set { this.billInformation = value; }
  102. }
  103. public string Trailer
  104. {
  105. get { return trailer; }
  106. }
  107. }
  108. public class Reference
  109. {
  110. private ReferenceType referenceType;
  111. private string reference;
  112. private ReferenceTextType? referenceTextType;
  113. public ReferenceType RefType
  114. {
  115. get { return referenceType; }
  116. set { referenceType = value; }
  117. }
  118. public string ReferenceText
  119. {
  120. get { return !string.IsNullOrEmpty(reference) ? reference.Replace("\n", "") : null; }
  121. set { reference = value; }
  122. }
  123. public ReferenceTextType? _ReferenceTextType
  124. {
  125. get { return referenceTextType; }
  126. set { referenceTextType = value; }
  127. }
  128. /// <summary>
  129. /// Creates a reference object which must be passed to the SwissQrCode instance
  130. /// </summary>
  131. /// <param name="referenceType">Type of the reference (QRR, SCOR or NON)</param>
  132. /// <param name="reference">Reference text</param>
  133. /// <param name="referenceTextType">Type of the reference text (QR-reference or Creditor Reference)</param>
  134. public Reference(ReferenceType referenceType, string reference, ReferenceTextType? referenceTextType)
  135. {
  136. MyRes res = new MyRes("Messages,Swiss");
  137. this.referenceType = referenceType;
  138. this.referenceTextType = referenceTextType;
  139. if (reference != null && reference.StartsWith("[") && reference.EndsWith("]"))
  140. {
  141. this.reference = reference;
  142. return;
  143. }
  144. string referenceCleaned = reference is null ? null : new string(reference?.Where(c => char.IsLetterOrDigit(c)).ToArray());
  145. if (referenceType == ReferenceType.NON && referenceCleaned != null)
  146. throw new SwissQrCodeException(res.Get("SwissRefTypeNon"));
  147. if (referenceType != ReferenceType.NON && referenceCleaned == null)
  148. throw new SwissQrCodeException(res.Get("SwissRefTypeNotNon"));
  149. if (referenceType != ReferenceType.NON && referenceCleaned != null && referenceTextType == null)
  150. throw new SwissQrCodeException(res.Get("SwissRefTextTypeNon"));
  151. if (referenceTextType == ReferenceTextType.QrReference && referenceCleaned != null && (referenceCleaned.Length > 27))
  152. throw new SwissQrCodeException(res.Get("SwissRefQRLength"));
  153. if (referenceTextType == ReferenceTextType.QrReference && referenceCleaned != null && !Regex.IsMatch(referenceCleaned, @"^[0-9]+$"))
  154. throw new SwissQrCodeException(res.Get("SwissRefQRNotOnlyDigits"));
  155. if (referenceTextType == ReferenceTextType.QrReference && referenceCleaned != null && !ChecksumMod10(referenceCleaned))
  156. throw new SwissQrCodeException(res.Get("SwissRefQRCheckSum"));
  157. if (referenceTextType == ReferenceTextType.CreditorReferenceIso11649 && referenceCleaned != null && (referenceCleaned.Length > 25))
  158. throw new SwissQrCodeException(res.Get("SwissRefISOLength"));
  159. this.reference = reference;
  160. }
  161. public Reference(string reference)
  162. {
  163. string[] data = reference.Split('\r');
  164. switch (data[0].Trim())
  165. {
  166. case nameof(ReferenceType.QRR):
  167. this.referenceType = ReferenceType.QRR;
  168. break;
  169. case nameof(ReferenceType.SCOR):
  170. this.referenceType = ReferenceType.SCOR;
  171. break;
  172. case nameof(ReferenceType.NON):
  173. this.referenceType = ReferenceType.NON;
  174. break;
  175. }
  176. this.reference = data[1].Trim();
  177. }
  178. /// <summary>
  179. /// Reference type. When using a QR-IBAN you have to use either "QRR" or "SCOR"
  180. /// </summary>
  181. public enum ReferenceType
  182. {
  183. QRR,
  184. SCOR,
  185. NON
  186. }
  187. public enum ReferenceTextType
  188. {
  189. QrReference,
  190. CreditorReferenceIso11649
  191. }
  192. public bool ChecksumMod10(string digits)
  193. {
  194. if (string.IsNullOrEmpty(digits) || digits.Length < 2)
  195. return false;
  196. if (digits.Any(char.IsLetter))
  197. return false;
  198. int[] mods = new int[] { 0, 9, 4, 6, 8, 2, 7, 1, 3, 5 };
  199. string digitsClean = digits.Replace(" ", "");
  200. int remainder = 0;
  201. for (int i = 0; i < digitsClean.Length - 1; i++)
  202. {
  203. int num = Convert.ToInt32(digitsClean[i]) - 48;
  204. remainder = mods[(num + remainder) % 10];
  205. }
  206. int checksum = (10 - remainder) % 10;
  207. return checksum == Convert.ToInt32(digitsClean[digitsClean.Length - 1]) - 48;
  208. }
  209. }
  210. public class Contact
  211. {
  212. private List<string> twoLetterCodes;
  213. private string br = "\r\n";
  214. private string name, streetOrAddressline1, houseNumberOrAddressline2, zipCode, city, country;
  215. private AddressType adrType;
  216. public string Name { get { return name; } set { name = value; } }
  217. public string StreetOrAddressline { get { return streetOrAddressline1; } set { streetOrAddressline1 = value; } }
  218. public string HouseNumberOrAddressline { get { return houseNumberOrAddressline2; } set { houseNumberOrAddressline2 = value; } }
  219. public string ZipCode { get { return zipCode; } set { zipCode = value; } }
  220. public string City { get { return city; } set { city = value; } }
  221. public string Country { get { return country; } set { country = value; } }
  222. /// <summary>
  223. /// Contact type. Can be used for payee, ultimate payee, etc. with address in structured mode (S).
  224. /// </summary>
  225. /// <param name="name">Last name or company (optional first name)</param>
  226. /// <param name="zipCode">Zip-/Postcode</param>
  227. /// <param name="city">City name</param>
  228. /// <param name="country">Two-letter country code as defined in ISO 3166-1</param>
  229. /// <param name="street">Streetname without house number</param>
  230. /// <param name="houseNumber">House number</param>
  231. public Contact(string name, string zipCode, string city, string country, string street, string houseNumber) : this(name, zipCode, city, country, street, houseNumber, AddressType.StructuredAddress)
  232. {
  233. }
  234. /// <summary>
  235. /// Contact type. Can be used for payee, ultimate payee, etc. with address in combined mode (K).
  236. /// </summary>
  237. /// <param name="name">Last name or company (optional first name)</param>
  238. /// <param name="country">Two-letter country code as defined in ISO 3166-1</param>
  239. /// <param name="addressLine1">Adress line 1</param>
  240. /// <param name="addressLine2">Adress line 2</param>
  241. public Contact(string name, string country, string addressLine1, string addressLine2) : this(name, null, null, country, addressLine1, addressLine2, AddressType.CombinedAddress)
  242. {
  243. }
  244. private Contact(string name, string zipCode, string city, string country, string streetOrAddressline1, string houseNumberOrAddressline2, AddressType addressType)
  245. {
  246. twoLetterCodes = ValidTwoLetterCodes();
  247. MyRes res = new MyRes("Messages,Swiss");
  248. this.adrType = addressType;
  249. if (string.IsNullOrEmpty(name))
  250. throw new SwissQrCodeContactException(String.Format(res.Get("SwissEmptyProperty"), res.Get("SwissPropName")));
  251. if (!name.StartsWith("[") || !name.EndsWith("]"))
  252. {
  253. if (name.Length > 70)
  254. throw new SwissQrCodeContactException(String.Format(res.Get("SwissLengthMore"), res.Get("SwissPropName"), 71));
  255. if (!Regex.IsMatch(name, QRSwissParameters.charsetPattern))
  256. throw new SwissQrCodeContactException(String.Format(res.Get("SwissPatternError"), res.Get("SwissPropName")) + QRSwissParameters.charsetPattern);
  257. }
  258. this.name = name;
  259. if (AddressType.StructuredAddress == this.adrType)
  260. {
  261. if (!streetOrAddressline1.StartsWith("[") || !streetOrAddressline1.EndsWith("]"))
  262. {
  263. if (!string.IsNullOrEmpty(streetOrAddressline1) && (streetOrAddressline1.Length > 70))
  264. throw new SwissQrCodeContactException(String.Format(res.Get("SwissLengthMore"), res.Get("SwissPropStreet"), 71));
  265. if (!string.IsNullOrEmpty(streetOrAddressline1) && !Regex.IsMatch(streetOrAddressline1, QRSwissParameters.charsetPattern))
  266. throw new SwissQrCodeContactException(String.Format(res.Get("SwissPatternError"), res.Get("SwissPropStreet")) + QRSwissParameters.charsetPattern);
  267. }
  268. this.streetOrAddressline1 = streetOrAddressline1;
  269. if (!houseNumberOrAddressline2.StartsWith("[") || !houseNumberOrAddressline2.EndsWith("]"))
  270. {
  271. if (!string.IsNullOrEmpty(houseNumberOrAddressline2) && houseNumberOrAddressline2.Length > 16)
  272. throw new SwissQrCodeContactException(String.Format(res.Get("SwissLengthMore"), res.Get("SwissPropHouseNumber"), 17));
  273. if (!string.IsNullOrEmpty(houseNumberOrAddressline2) && !Regex.IsMatch(houseNumberOrAddressline2, QRSwissParameters.charsetPattern))
  274. throw new SwissQrCodeContactException(String.Format(res.Get("SwissPatternError"), res.Get("SwissPropStreet")) + QRSwissParameters.charsetPattern);
  275. }
  276. this.houseNumberOrAddressline2 = houseNumberOrAddressline2;
  277. }
  278. else
  279. {
  280. if (!string.IsNullOrEmpty(streetOrAddressline1) && (streetOrAddressline1.Length > 70))
  281. throw new SwissQrCodeContactException(String.Format(res.Get("SwissLengthMore"), "Address line 1", 71));
  282. if (!string.IsNullOrEmpty(streetOrAddressline1) && !Regex.IsMatch(streetOrAddressline1, QRSwissParameters.charsetPattern))
  283. throw new SwissQrCodeContactException(String.Format(res.Get("SwissPatternError"), "Address line 1") + QRSwissParameters.charsetPattern);
  284. this.streetOrAddressline1 = streetOrAddressline1;
  285. if (string.IsNullOrEmpty(houseNumberOrAddressline2))
  286. throw new SwissQrCodeContactException(res.Get("SwissAddressLine2Error"));
  287. if (!string.IsNullOrEmpty(houseNumberOrAddressline2) && (houseNumberOrAddressline2.Length > 70))
  288. throw new SwissQrCodeContactException(String.Format(res.Get("SwissLengthMore"), "Address line 2", 71));
  289. if (!string.IsNullOrEmpty(houseNumberOrAddressline2) && !Regex.IsMatch(houseNumberOrAddressline2, QRSwissParameters.charsetPattern))
  290. throw new SwissQrCodeContactException(String.Format(res.Get("SwissPatternError"), "Address line 2") + QRSwissParameters.charsetPattern);
  291. this.houseNumberOrAddressline2 = houseNumberOrAddressline2;
  292. }
  293. if (AddressType.StructuredAddress == this.adrType)
  294. {
  295. if (!zipCode.StartsWith("[") || !zipCode.EndsWith("]"))
  296. {
  297. if (string.IsNullOrEmpty(zipCode))
  298. throw new SwissQrCodeContactException(String.Format(res.Get("SwissEmptyProperty"), res.Get("SwissPropZipCode")));
  299. if (zipCode.Length > 16)
  300. throw new SwissQrCodeContactException(String.Format(res.Get("SwissLengthMore"), res.Get("SwissPropZipCode"), 17));
  301. if (!Regex.IsMatch(zipCode, QRSwissParameters.charsetPattern))
  302. throw new SwissQrCodeContactException(String.Format(res.Get("SwissPatternError"), res.Get("SwissPropZipCode")) + QRSwissParameters.charsetPattern);
  303. }
  304. this.zipCode = zipCode;
  305. if (!city.StartsWith("[") || !city.EndsWith("]"))
  306. {
  307. if (string.IsNullOrEmpty(city))
  308. throw new SwissQrCodeContactException(String.Format(res.Get("SwissEmptyProperty"), res.Get("SwissPropCity")));
  309. if (city.Length > 35)
  310. throw new SwissQrCodeContactException(String.Format(res.Get("SwissLengthMore"), res.Get("SwissPropCity"), 36));
  311. if (!Regex.IsMatch(city, QRSwissParameters.charsetPattern))
  312. throw new SwissQrCodeContactException(String.Format(res.Get("SwissPatternError"), res.Get("SwissPropCity")) + QRSwissParameters.charsetPattern);
  313. }
  314. this.city = city;
  315. }
  316. else
  317. {
  318. this.zipCode = this.city = string.Empty;
  319. }
  320. if (!country.StartsWith("[") || !country.EndsWith("]"))
  321. if (!IsValidTwoLetterCode(country))
  322. throw new SwissQrCodeContactException(res.Get("SwissCountryTwoLetters"));
  323. this.country = country;
  324. }
  325. private bool IsValidTwoLetterCode(string code)
  326. {
  327. return twoLetterCodes.Contains(code);
  328. }
  329. private List<string> ValidTwoLetterCodes()
  330. {
  331. string[] codes = new string[] { "AF", "AL", "DZ", "AS", "AD", "AO", "AI", "AQ", "AG", "AR", "AM", "AW", "AU", "AT", "AZ", "BS", "BH", "BD", "BB", "BY", "BE", "BZ", "BJ", "BM", "BT", "BO", "BQ", "BA", "BW", "BV", "BR", "IO", "BN", "BG", "BF", "BI", "CV", "KH", "CM", "CA", "KY", "CF", "TD", "CL", "CN", "CX", "CC", "CO", "KM", "CG", "CD", "CK", "CR", "CI", "HR", "CU", "CW", "CY", "CZ", "DK", "DJ", "DM", "DO", "EC", "EG", "SV", "GQ", "ER", "EE", "SZ", "ET", "FK", "FO", "FJ", "FI", "FR", "GF", "PF", "TF", "GA", "GM", "GE", "DE", "GH", "GI", "GR", "GL", "GD", "GP", "GU", "GT", "GG", "GN", "GW", "GY", "HT", "HM", "VA", "HN", "HK", "HU", "IS", "IN", "ID", "IR", "IQ", "IE", "IM", "IL", "IT", "JM", "JP", "JE", "JO", "KZ", "KE", "KI", "KP", "KR", "KW", "KG", "LA", "LV", "LB", "LS", "LR", "LY", "LI", "LT", "LU", "MO", "MG", "MW", "MY", "MV", "ML", "MT", "MH", "MQ", "MR", "MU", "YT", "MX", "FM", "MD", "MC", "MN", "ME", "MS", "MA", "MZ", "MM", "NA", "NR", "NP", "NL", "NC", "NZ", "NI", "NE", "NG", "NU", "NF", "MP", "MK", "NO", "OM", "PK", "PW", "PS", "PA", "PG", "PY", "PE", "PH", "PN", "PL", "PT", "PR", "QA", "RE", "RO", "RU", "RW", "BL", "SH", "KN", "LC", "MF", "PM", "VC", "WS", "SM", "ST", "SA", "SN", "RS", "SC", "SL", "SG", "SX", "SK", "SI", "SB", "SO", "ZA", "GS", "SS", "ES", "LK", "SD", "SR", "SJ", "SE", "CH", "SY", "TW", "TJ", "TZ", "TH", "TL", "TG", "TK", "TO", "TT", "TN", "TR", "TM", "TC", "TV", "UG", "UA", "AE", "GB", "US", "UM", "UY", "UZ", "VU", "VE", "VN", "VG", "VI", "WF", "EH", "YE", "ZM", "ZW", "AX" };
  332. List<string> codesList = new List<string>();
  333. foreach (string str in codes)
  334. {
  335. codesList.Add(str);
  336. }
  337. return codesList;
  338. }
  339. public Contact(string contact)
  340. {
  341. string[] data = contact.Split('\r');
  342. if (data[0].Trim() == "S")
  343. this.adrType = AddressType.StructuredAddress;
  344. else
  345. this.adrType = AddressType.CombinedAddress;
  346. name = data[1].Trim();
  347. streetOrAddressline1 = data[2].Trim();
  348. houseNumberOrAddressline2 = data[3].Trim();
  349. zipCode = data[4].Trim();
  350. city = data[5].Trim();
  351. country = data[6].Trim();
  352. }
  353. public override string ToString()
  354. {
  355. string contactData = ""; //AdrTp
  356. if (AddressType.StructuredAddress == adrType)
  357. contactData += "S";
  358. else
  359. contactData += "K";
  360. contactData += br;
  361. contactData += name.Replace("\n", "") + br; //Name
  362. contactData += (!string.IsNullOrEmpty(streetOrAddressline1) ? streetOrAddressline1.Replace("\n", "") : string.Empty) + br; //StrtNmOrAdrLine1
  363. contactData += (!string.IsNullOrEmpty(houseNumberOrAddressline2) ? houseNumberOrAddressline2.Replace("\n", "") : string.Empty) + br; //BldgNbOrAdrLine2
  364. contactData += zipCode.Replace("\n", "") + br; //PstCd
  365. contactData += city.Replace("\n", "") + br; //TwnNm
  366. contactData += country + br; //Ctry
  367. return contactData;
  368. }
  369. public enum AddressType
  370. {
  371. StructuredAddress,
  372. CombinedAddress
  373. }
  374. public class SwissQrCodeContactException : SwissQrCodeException
  375. {
  376. public SwissQrCodeContactException()
  377. {
  378. }
  379. public SwissQrCodeContactException(string message)
  380. : base(message)
  381. {
  382. }
  383. public SwissQrCodeContactException(string message, SwissQrCodeException inner)
  384. : base(message, inner)
  385. {
  386. }
  387. }
  388. }
  389. public class Iban
  390. {
  391. private string iban;
  392. private IbanType ibanType;
  393. public IbanType TypeIban { get { return ibanType; } set { ibanType = value; } }
  394. public string _Iban { get { return iban; } set { iban = value; } }
  395. /// <summary>
  396. /// IBAN object with type information
  397. /// </summary>
  398. /// <param name="iban">IBAN</param>
  399. /// <param name="ibanType">Type of IBAN (normal or QR-IBAN)</param>
  400. public Iban(string iban, IbanType ibanType)
  401. {
  402. MyRes res = new MyRes("Messages,Swiss");
  403. if (ibanType == IbanType.Iban && !IsValidIban(iban))
  404. throw new SwissQrCodeException(res.Get("SwissIbanNotValid"));
  405. if (ibanType == IbanType.QrIban && !IsValidQRIban(iban))
  406. throw new SwissQrCodeException(res.Get("SwissQRIbanNotValid"));
  407. if (!iban.StartsWith("CH", StringComparison.OrdinalIgnoreCase) && !iban.StartsWith("LI", StringComparison.OrdinalIgnoreCase))
  408. throw new SwissQrCodeException(res.Get("SwissQRStartNotValid"));
  409. this.iban = iban;
  410. this.ibanType = ibanType;
  411. }
  412. public bool IsQrIban
  413. {
  414. get { return ibanType == IbanType.QrIban; }
  415. }
  416. public Iban(string iban)
  417. {
  418. this.iban = iban;
  419. }
  420. public override string ToString()
  421. {
  422. if (iban.StartsWith("[") && iban.EndsWith("]"))
  423. return iban;
  424. else
  425. return new string(iban.Where(c => char.IsLetterOrDigit(c)).ToArray()).ToUpper();
  426. }
  427. public enum IbanType
  428. {
  429. Iban,
  430. QrIban
  431. }
  432. private bool IsValidIban(string iban)
  433. {
  434. //Clean IBAN
  435. string ibanCleared = new string(iban.Where(c => char.IsLetterOrDigit(c)).ToArray()).ToUpper();
  436. //Check for general structure
  437. bool structurallyValid = Regex.IsMatch(ibanCleared, @"^[a-zA-Z]{2}[0-9]{2}([a-zA-Z0-9]?){16,30}$");
  438. //Check IBAN checksum
  439. char[] charSum = (ibanCleared.Substring(4) + ibanCleared.Substring(0, 4)).ToCharArray();
  440. string sum = "";
  441. foreach (char c in charSum)
  442. {
  443. sum += (char.IsLetter(c) ? (c - 55).ToString() : c.ToString());
  444. }
  445. decimal sumDec;
  446. if (!decimal.TryParse(sum, out sumDec))
  447. return false;
  448. bool checksumValid = (sumDec % 97) == 1;
  449. return structurallyValid && checksumValid;
  450. }
  451. private bool IsValidQRIban(string iban)
  452. {
  453. bool foundQrIid = false;
  454. try
  455. {
  456. string ibanCleared = new string(iban.Where(c => char.IsLetterOrDigit(c)).ToArray()).ToUpper();
  457. int possibleQrIid = Convert.ToInt32(ibanCleared.Substring(4, 5));
  458. foundQrIid = possibleQrIid >= 30000 && possibleQrIid <= 31999;
  459. }
  460. catch { }
  461. return IsValidIban(iban) && foundQrIid;
  462. }
  463. }
  464. public enum Currency
  465. {
  466. CHF = 756,
  467. EUR = 978
  468. }
  469. }