├── .gitignore ├── .travis.yml ├── AdyenCSE.podspec ├── AdyenCSE.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ └── xcschemes │ └── AdyenCSE.xcscheme ├── AdyenCSE ├── ADYAESCCMCryptor.h ├── ADYAESCCMCryptor.m ├── ADYCard.h ├── ADYCard.m ├── ADYCryptor.h ├── ADYCryptor.m ├── ADYEncrypter.h ├── ADYEncrypter.m ├── ADYRSACryptor.h ├── ADYRSACryptor.m ├── AdyenCSE.h ├── Info.plist ├── NSDictionary+AdyenUtil.h ├── NSDictionary+AdyenUtil.m ├── NSString+AdyenURLEncoding.h ├── NSString+AdyenURLEncoding.m ├── NSString+AdyenUtil.h └── NSString+AdyenUtil.m ├── Example ├── Example.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata ├── Example.xcworkspace │ └── contents.xcworkspacedata ├── Example │ ├── ADYAppDelegate.h │ ├── ADYAppDelegate.m │ ├── ADYViewController.h │ ├── ADYViewController.m │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Info.plist │ ├── Launch Screen.storyboard │ ├── Main.storyboard │ └── main.m └── Podfile ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | # Xcode 4 | # 5 | build/ 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata 15 | *.xccheckout 16 | *.moved-aside 17 | DerivedData 18 | *.hmap 19 | *.ipa 20 | *.xcuserstate 21 | Example/Pods/ 22 | Example/Podfile.lock 23 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | -------------------------------------------------------------------------------- /AdyenCSE.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'AdyenCSE' 3 | s.version = '1.1.1' 4 | s.summary = 'AdyenCSE is a client side encryption library for credit card data.' 5 | s.description = <<-DESC 6 | With CSE card data is encrypted within the client, in this case the iOS device, before you submit it via your own server to the Adyen API. By using CSE you reduce your scope of PCI compliance, because no raw card data travels through your server. This repository can be leveraged as a starting point to integrate Adyen's payment functionality fully in-app. 7 | DESC 8 | s.homepage = 'https://github.com/Adyen/AdyenCSE-iOS' 9 | s.license = { :type => 'MIT', :file => 'LICENSE' } 10 | s.author = { 'Adyen' => 'support@adyen.com' } 11 | s.source = { :git => 'https://github.com/Adyen/AdyenCSE-iOS.git', :tag => "#{s.version}" } 12 | s.ios.deployment_target = '7.0' 13 | s.source_files = 'AdyenCSE/**/*.{h,m}' 14 | s.frameworks = 'Foundation', 'Security' 15 | end 16 | -------------------------------------------------------------------------------- /AdyenCSE.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | E20AD1941EFBE0220065B70E /* AdyenCSE.h in Headers */ = {isa = PBXBuildFile; fileRef = E20AD1921EFBE0220065B70E /* AdyenCSE.h */; settings = {ATTRIBUTES = (Public, ); }; }; 11 | E20AD1AA1EFBE0830065B70E /* ADYAESCCMCryptor.h in Headers */ = {isa = PBXBuildFile; fileRef = E20AD19A1EFBE0830065B70E /* ADYAESCCMCryptor.h */; settings = {ATTRIBUTES = (Public, ); }; }; 12 | E20AD1AB1EFBE0830065B70E /* ADYAESCCMCryptor.m in Sources */ = {isa = PBXBuildFile; fileRef = E20AD19B1EFBE0830065B70E /* ADYAESCCMCryptor.m */; }; 13 | E20AD1AC1EFBE0830065B70E /* ADYCard.h in Headers */ = {isa = PBXBuildFile; fileRef = E20AD19C1EFBE0830065B70E /* ADYCard.h */; settings = {ATTRIBUTES = (Public, ); }; }; 14 | E20AD1AD1EFBE0830065B70E /* ADYCard.m in Sources */ = {isa = PBXBuildFile; fileRef = E20AD19D1EFBE0830065B70E /* ADYCard.m */; }; 15 | E20AD1AE1EFBE0830065B70E /* ADYCryptor.h in Headers */ = {isa = PBXBuildFile; fileRef = E20AD19E1EFBE0830065B70E /* ADYCryptor.h */; settings = {ATTRIBUTES = (Public, ); }; }; 16 | E20AD1AF1EFBE0830065B70E /* ADYCryptor.m in Sources */ = {isa = PBXBuildFile; fileRef = E20AD19F1EFBE0830065B70E /* ADYCryptor.m */; }; 17 | E20AD1B01EFBE0830065B70E /* ADYEncrypter.h in Headers */ = {isa = PBXBuildFile; fileRef = E20AD1A01EFBE0830065B70E /* ADYEncrypter.h */; settings = {ATTRIBUTES = (Public, ); }; }; 18 | E20AD1B11EFBE0830065B70E /* ADYEncrypter.m in Sources */ = {isa = PBXBuildFile; fileRef = E20AD1A11EFBE0830065B70E /* ADYEncrypter.m */; }; 19 | E20AD1B21EFBE0830065B70E /* ADYRSACryptor.h in Headers */ = {isa = PBXBuildFile; fileRef = E20AD1A21EFBE0830065B70E /* ADYRSACryptor.h */; settings = {ATTRIBUTES = (Public, ); }; }; 20 | E20AD1B31EFBE0830065B70E /* ADYRSACryptor.m in Sources */ = {isa = PBXBuildFile; fileRef = E20AD1A31EFBE0830065B70E /* ADYRSACryptor.m */; }; 21 | E20AD1B41EFBE0830065B70E /* NSDictionary+AdyenUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = E20AD1A41EFBE0830065B70E /* NSDictionary+AdyenUtil.h */; settings = {ATTRIBUTES = (Public, ); }; }; 22 | E20AD1B51EFBE0830065B70E /* NSDictionary+AdyenUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = E20AD1A51EFBE0830065B70E /* NSDictionary+AdyenUtil.m */; }; 23 | E20AD1B61EFBE0830065B70E /* NSString+AdyenURLEncoding.h in Headers */ = {isa = PBXBuildFile; fileRef = E20AD1A61EFBE0830065B70E /* NSString+AdyenURLEncoding.h */; settings = {ATTRIBUTES = (Public, ); }; }; 24 | E20AD1B71EFBE0830065B70E /* NSString+AdyenURLEncoding.m in Sources */ = {isa = PBXBuildFile; fileRef = E20AD1A71EFBE0830065B70E /* NSString+AdyenURLEncoding.m */; }; 25 | E20AD1B81EFBE0830065B70E /* NSString+AdyenUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = E20AD1A81EFBE0830065B70E /* NSString+AdyenUtil.h */; settings = {ATTRIBUTES = (Public, ); }; }; 26 | E20AD1B91EFBE0830065B70E /* NSString+AdyenUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = E20AD1A91EFBE0830065B70E /* NSString+AdyenUtil.m */; }; 27 | /* End PBXBuildFile section */ 28 | 29 | /* Begin PBXFileReference section */ 30 | E20AD18F1EFBE0220065B70E /* AdyenCSE.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AdyenCSE.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 31 | E20AD1921EFBE0220065B70E /* AdyenCSE.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AdyenCSE.h; sourceTree = ""; }; 32 | E20AD1931EFBE0220065B70E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 33 | E20AD19A1EFBE0830065B70E /* ADYAESCCMCryptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ADYAESCCMCryptor.h; sourceTree = ""; }; 34 | E20AD19B1EFBE0830065B70E /* ADYAESCCMCryptor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ADYAESCCMCryptor.m; sourceTree = ""; }; 35 | E20AD19C1EFBE0830065B70E /* ADYCard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ADYCard.h; sourceTree = ""; }; 36 | E20AD19D1EFBE0830065B70E /* ADYCard.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ADYCard.m; sourceTree = ""; }; 37 | E20AD19E1EFBE0830065B70E /* ADYCryptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ADYCryptor.h; sourceTree = ""; }; 38 | E20AD19F1EFBE0830065B70E /* ADYCryptor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ADYCryptor.m; sourceTree = ""; }; 39 | E20AD1A01EFBE0830065B70E /* ADYEncrypter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ADYEncrypter.h; sourceTree = ""; }; 40 | E20AD1A11EFBE0830065B70E /* ADYEncrypter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ADYEncrypter.m; sourceTree = ""; }; 41 | E20AD1A21EFBE0830065B70E /* ADYRSACryptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ADYRSACryptor.h; sourceTree = ""; }; 42 | E20AD1A31EFBE0830065B70E /* ADYRSACryptor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ADYRSACryptor.m; sourceTree = ""; }; 43 | E20AD1A41EFBE0830065B70E /* NSDictionary+AdyenUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+AdyenUtil.h"; sourceTree = ""; }; 44 | E20AD1A51EFBE0830065B70E /* NSDictionary+AdyenUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+AdyenUtil.m"; sourceTree = ""; }; 45 | E20AD1A61EFBE0830065B70E /* NSString+AdyenURLEncoding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+AdyenURLEncoding.h"; sourceTree = ""; }; 46 | E20AD1A71EFBE0830065B70E /* NSString+AdyenURLEncoding.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+AdyenURLEncoding.m"; sourceTree = ""; }; 47 | E20AD1A81EFBE0830065B70E /* NSString+AdyenUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+AdyenUtil.h"; sourceTree = ""; }; 48 | E20AD1A91EFBE0830065B70E /* NSString+AdyenUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+AdyenUtil.m"; sourceTree = ""; }; 49 | /* End PBXFileReference section */ 50 | 51 | /* Begin PBXFrameworksBuildPhase section */ 52 | E20AD18B1EFBE0220065B70E /* Frameworks */ = { 53 | isa = PBXFrameworksBuildPhase; 54 | buildActionMask = 2147483647; 55 | files = ( 56 | ); 57 | runOnlyForDeploymentPostprocessing = 0; 58 | }; 59 | /* End PBXFrameworksBuildPhase section */ 60 | 61 | /* Begin PBXGroup section */ 62 | E20AD1851EFBE0220065B70E = { 63 | isa = PBXGroup; 64 | children = ( 65 | E20AD1911EFBE0220065B70E /* AdyenCSE */, 66 | E20AD1901EFBE0220065B70E /* Products */, 67 | ); 68 | sourceTree = ""; 69 | }; 70 | E20AD1901EFBE0220065B70E /* Products */ = { 71 | isa = PBXGroup; 72 | children = ( 73 | E20AD18F1EFBE0220065B70E /* AdyenCSE.framework */, 74 | ); 75 | name = Products; 76 | sourceTree = ""; 77 | }; 78 | E20AD1911EFBE0220065B70E /* AdyenCSE */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | E20AD1921EFBE0220065B70E /* AdyenCSE.h */, 82 | E20AD19A1EFBE0830065B70E /* ADYAESCCMCryptor.h */, 83 | E20AD19B1EFBE0830065B70E /* ADYAESCCMCryptor.m */, 84 | E20AD19C1EFBE0830065B70E /* ADYCard.h */, 85 | E20AD19D1EFBE0830065B70E /* ADYCard.m */, 86 | E20AD19E1EFBE0830065B70E /* ADYCryptor.h */, 87 | E20AD19F1EFBE0830065B70E /* ADYCryptor.m */, 88 | E20AD1A01EFBE0830065B70E /* ADYEncrypter.h */, 89 | E20AD1A11EFBE0830065B70E /* ADYEncrypter.m */, 90 | E20AD1A21EFBE0830065B70E /* ADYRSACryptor.h */, 91 | E20AD1A31EFBE0830065B70E /* ADYRSACryptor.m */, 92 | E20AD1A41EFBE0830065B70E /* NSDictionary+AdyenUtil.h */, 93 | E20AD1A51EFBE0830065B70E /* NSDictionary+AdyenUtil.m */, 94 | E20AD1A61EFBE0830065B70E /* NSString+AdyenURLEncoding.h */, 95 | E20AD1A71EFBE0830065B70E /* NSString+AdyenURLEncoding.m */, 96 | E20AD1A81EFBE0830065B70E /* NSString+AdyenUtil.h */, 97 | E20AD1A91EFBE0830065B70E /* NSString+AdyenUtil.m */, 98 | E20AD1931EFBE0220065B70E /* Info.plist */, 99 | ); 100 | path = AdyenCSE; 101 | sourceTree = ""; 102 | }; 103 | /* End PBXGroup section */ 104 | 105 | /* Begin PBXHeadersBuildPhase section */ 106 | E20AD18C1EFBE0220065B70E /* Headers */ = { 107 | isa = PBXHeadersBuildPhase; 108 | buildActionMask = 2147483647; 109 | files = ( 110 | E20AD1941EFBE0220065B70E /* AdyenCSE.h in Headers */, 111 | E20AD1B01EFBE0830065B70E /* ADYEncrypter.h in Headers */, 112 | E20AD1B21EFBE0830065B70E /* ADYRSACryptor.h in Headers */, 113 | E20AD1AC1EFBE0830065B70E /* ADYCard.h in Headers */, 114 | E20AD1B81EFBE0830065B70E /* NSString+AdyenUtil.h in Headers */, 115 | E20AD1B41EFBE0830065B70E /* NSDictionary+AdyenUtil.h in Headers */, 116 | E20AD1AA1EFBE0830065B70E /* ADYAESCCMCryptor.h in Headers */, 117 | E20AD1AE1EFBE0830065B70E /* ADYCryptor.h in Headers */, 118 | E20AD1B61EFBE0830065B70E /* NSString+AdyenURLEncoding.h in Headers */, 119 | ); 120 | runOnlyForDeploymentPostprocessing = 0; 121 | }; 122 | /* End PBXHeadersBuildPhase section */ 123 | 124 | /* Begin PBXNativeTarget section */ 125 | E20AD18E1EFBE0220065B70E /* AdyenCSE */ = { 126 | isa = PBXNativeTarget; 127 | buildConfigurationList = E20AD1971EFBE0220065B70E /* Build configuration list for PBXNativeTarget "AdyenCSE" */; 128 | buildPhases = ( 129 | E20AD18A1EFBE0220065B70E /* Sources */, 130 | E20AD18B1EFBE0220065B70E /* Frameworks */, 131 | E20AD18C1EFBE0220065B70E /* Headers */, 132 | E20AD18D1EFBE0220065B70E /* Resources */, 133 | ); 134 | buildRules = ( 135 | ); 136 | dependencies = ( 137 | ); 138 | name = AdyenCSE; 139 | productName = AdyenCSE; 140 | productReference = E20AD18F1EFBE0220065B70E /* AdyenCSE.framework */; 141 | productType = "com.apple.product-type.framework"; 142 | }; 143 | /* End PBXNativeTarget section */ 144 | 145 | /* Begin PBXProject section */ 146 | E20AD1861EFBE0220065B70E /* Project object */ = { 147 | isa = PBXProject; 148 | attributes = { 149 | CLASSPREFIX = ADY; 150 | LastUpgradeCheck = 0900; 151 | ORGANIZATIONNAME = Adyen; 152 | TargetAttributes = { 153 | E20AD18E1EFBE0220065B70E = { 154 | CreatedOnToolsVersion = 8.3.3; 155 | ProvisioningStyle = Automatic; 156 | }; 157 | }; 158 | }; 159 | buildConfigurationList = E20AD1891EFBE0220065B70E /* Build configuration list for PBXProject "AdyenCSE" */; 160 | compatibilityVersion = "Xcode 3.2"; 161 | developmentRegion = English; 162 | hasScannedForEncodings = 0; 163 | knownRegions = ( 164 | en, 165 | ); 166 | mainGroup = E20AD1851EFBE0220065B70E; 167 | productRefGroup = E20AD1901EFBE0220065B70E /* Products */; 168 | projectDirPath = ""; 169 | projectRoot = ""; 170 | targets = ( 171 | E20AD18E1EFBE0220065B70E /* AdyenCSE */, 172 | ); 173 | }; 174 | /* End PBXProject section */ 175 | 176 | /* Begin PBXResourcesBuildPhase section */ 177 | E20AD18D1EFBE0220065B70E /* Resources */ = { 178 | isa = PBXResourcesBuildPhase; 179 | buildActionMask = 2147483647; 180 | files = ( 181 | ); 182 | runOnlyForDeploymentPostprocessing = 0; 183 | }; 184 | /* End PBXResourcesBuildPhase section */ 185 | 186 | /* Begin PBXSourcesBuildPhase section */ 187 | E20AD18A1EFBE0220065B70E /* Sources */ = { 188 | isa = PBXSourcesBuildPhase; 189 | buildActionMask = 2147483647; 190 | files = ( 191 | E20AD1B71EFBE0830065B70E /* NSString+AdyenURLEncoding.m in Sources */, 192 | E20AD1B91EFBE0830065B70E /* NSString+AdyenUtil.m in Sources */, 193 | E20AD1AF1EFBE0830065B70E /* ADYCryptor.m in Sources */, 194 | E20AD1AD1EFBE0830065B70E /* ADYCard.m in Sources */, 195 | E20AD1B51EFBE0830065B70E /* NSDictionary+AdyenUtil.m in Sources */, 196 | E20AD1AB1EFBE0830065B70E /* ADYAESCCMCryptor.m in Sources */, 197 | E20AD1B31EFBE0830065B70E /* ADYRSACryptor.m in Sources */, 198 | E20AD1B11EFBE0830065B70E /* ADYEncrypter.m in Sources */, 199 | ); 200 | runOnlyForDeploymentPostprocessing = 0; 201 | }; 202 | /* End PBXSourcesBuildPhase section */ 203 | 204 | /* Begin XCBuildConfiguration section */ 205 | E20AD1951EFBE0220065B70E /* Debug */ = { 206 | isa = XCBuildConfiguration; 207 | buildSettings = { 208 | ALWAYS_SEARCH_USER_PATHS = NO; 209 | CLANG_ANALYZER_NONNULL = YES; 210 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 211 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 212 | CLANG_CXX_LIBRARY = "libc++"; 213 | CLANG_ENABLE_MODULES = YES; 214 | CLANG_ENABLE_OBJC_ARC = YES; 215 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 216 | CLANG_WARN_BOOL_CONVERSION = YES; 217 | CLANG_WARN_COMMA = YES; 218 | CLANG_WARN_CONSTANT_CONVERSION = YES; 219 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 220 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 221 | CLANG_WARN_EMPTY_BODY = YES; 222 | CLANG_WARN_ENUM_CONVERSION = YES; 223 | CLANG_WARN_INFINITE_RECURSION = YES; 224 | CLANG_WARN_INT_CONVERSION = YES; 225 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 226 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 227 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 228 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 229 | CLANG_WARN_STRICT_PROTOTYPES = YES; 230 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 231 | CLANG_WARN_UNREACHABLE_CODE = YES; 232 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 233 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 234 | COPY_PHASE_STRIP = NO; 235 | CURRENT_PROJECT_VERSION = 1; 236 | DEBUG_INFORMATION_FORMAT = dwarf; 237 | ENABLE_STRICT_OBJC_MSGSEND = YES; 238 | ENABLE_TESTABILITY = YES; 239 | GCC_C_LANGUAGE_STANDARD = gnu99; 240 | GCC_DYNAMIC_NO_PIC = NO; 241 | GCC_NO_COMMON_BLOCKS = YES; 242 | GCC_OPTIMIZATION_LEVEL = 0; 243 | GCC_PREPROCESSOR_DEFINITIONS = ( 244 | "DEBUG=1", 245 | "$(inherited)", 246 | ); 247 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 248 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 249 | GCC_WARN_UNDECLARED_SELECTOR = YES; 250 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 251 | GCC_WARN_UNUSED_FUNCTION = YES; 252 | GCC_WARN_UNUSED_VARIABLE = YES; 253 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 254 | MTL_ENABLE_DEBUG_INFO = YES; 255 | ONLY_ACTIVE_ARCH = YES; 256 | SDKROOT = iphoneos; 257 | TARGETED_DEVICE_FAMILY = "1,2"; 258 | VERSIONING_SYSTEM = "apple-generic"; 259 | VERSION_INFO_PREFIX = ""; 260 | }; 261 | name = Debug; 262 | }; 263 | E20AD1961EFBE0220065B70E /* Release */ = { 264 | isa = XCBuildConfiguration; 265 | buildSettings = { 266 | ALWAYS_SEARCH_USER_PATHS = NO; 267 | CLANG_ANALYZER_NONNULL = YES; 268 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 269 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 270 | CLANG_CXX_LIBRARY = "libc++"; 271 | CLANG_ENABLE_MODULES = YES; 272 | CLANG_ENABLE_OBJC_ARC = YES; 273 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 274 | CLANG_WARN_BOOL_CONVERSION = YES; 275 | CLANG_WARN_COMMA = YES; 276 | CLANG_WARN_CONSTANT_CONVERSION = YES; 277 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 278 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 279 | CLANG_WARN_EMPTY_BODY = YES; 280 | CLANG_WARN_ENUM_CONVERSION = YES; 281 | CLANG_WARN_INFINITE_RECURSION = YES; 282 | CLANG_WARN_INT_CONVERSION = YES; 283 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 284 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 285 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 286 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 287 | CLANG_WARN_STRICT_PROTOTYPES = YES; 288 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 289 | CLANG_WARN_UNREACHABLE_CODE = YES; 290 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 291 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 292 | COPY_PHASE_STRIP = NO; 293 | CURRENT_PROJECT_VERSION = 1; 294 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 295 | ENABLE_NS_ASSERTIONS = NO; 296 | ENABLE_STRICT_OBJC_MSGSEND = YES; 297 | GCC_C_LANGUAGE_STANDARD = gnu99; 298 | GCC_NO_COMMON_BLOCKS = YES; 299 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 300 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 301 | GCC_WARN_UNDECLARED_SELECTOR = YES; 302 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 303 | GCC_WARN_UNUSED_FUNCTION = YES; 304 | GCC_WARN_UNUSED_VARIABLE = YES; 305 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 306 | MTL_ENABLE_DEBUG_INFO = NO; 307 | SDKROOT = iphoneos; 308 | TARGETED_DEVICE_FAMILY = "1,2"; 309 | VALIDATE_PRODUCT = YES; 310 | VERSIONING_SYSTEM = "apple-generic"; 311 | VERSION_INFO_PREFIX = ""; 312 | }; 313 | name = Release; 314 | }; 315 | E20AD1981EFBE0220065B70E /* Debug */ = { 316 | isa = XCBuildConfiguration; 317 | buildSettings = { 318 | CODE_SIGN_IDENTITY = ""; 319 | DEFINES_MODULE = YES; 320 | DYLIB_COMPATIBILITY_VERSION = 1; 321 | DYLIB_CURRENT_VERSION = 1; 322 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 323 | INFOPLIST_FILE = AdyenCSE/Info.plist; 324 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 325 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 326 | PRODUCT_BUNDLE_IDENTIFIER = com.adyen.AdyenCSE; 327 | PRODUCT_NAME = "$(TARGET_NAME)"; 328 | SKIP_INSTALL = YES; 329 | }; 330 | name = Debug; 331 | }; 332 | E20AD1991EFBE0220065B70E /* Release */ = { 333 | isa = XCBuildConfiguration; 334 | buildSettings = { 335 | CODE_SIGN_IDENTITY = ""; 336 | DEFINES_MODULE = YES; 337 | DYLIB_COMPATIBILITY_VERSION = 1; 338 | DYLIB_CURRENT_VERSION = 1; 339 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 340 | INFOPLIST_FILE = AdyenCSE/Info.plist; 341 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 342 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 343 | PRODUCT_BUNDLE_IDENTIFIER = com.adyen.AdyenCSE; 344 | PRODUCT_NAME = "$(TARGET_NAME)"; 345 | SKIP_INSTALL = YES; 346 | }; 347 | name = Release; 348 | }; 349 | /* End XCBuildConfiguration section */ 350 | 351 | /* Begin XCConfigurationList section */ 352 | E20AD1891EFBE0220065B70E /* Build configuration list for PBXProject "AdyenCSE" */ = { 353 | isa = XCConfigurationList; 354 | buildConfigurations = ( 355 | E20AD1951EFBE0220065B70E /* Debug */, 356 | E20AD1961EFBE0220065B70E /* Release */, 357 | ); 358 | defaultConfigurationIsVisible = 0; 359 | defaultConfigurationName = Release; 360 | }; 361 | E20AD1971EFBE0220065B70E /* Build configuration list for PBXNativeTarget "AdyenCSE" */ = { 362 | isa = XCConfigurationList; 363 | buildConfigurations = ( 364 | E20AD1981EFBE0220065B70E /* Debug */, 365 | E20AD1991EFBE0220065B70E /* Release */, 366 | ); 367 | defaultConfigurationIsVisible = 0; 368 | defaultConfigurationName = Release; 369 | }; 370 | /* End XCConfigurationList section */ 371 | }; 372 | rootObject = E20AD1861EFBE0220065B70E /* Project object */; 373 | } 374 | -------------------------------------------------------------------------------- /AdyenCSE.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /AdyenCSE.xcodeproj/xcshareddata/xcschemes/AdyenCSE.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 35 | 36 | 47 | 48 | 54 | 55 | 56 | 57 | 58 | 59 | 65 | 66 | 72 | 73 | 74 | 75 | 77 | 78 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /AdyenCSE/ADYAESCCMCryptor.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017 Adyen B.V. 3 | // 4 | // This file is open source and available under the MIT license. See the LICENSE file for more info. 5 | // 6 | 7 | #import 8 | 9 | NS_ASSUME_NONNULL_BEGIN 10 | 11 | #define ADYC_AESCCM_TraceLog 0 12 | 13 | @interface ADYAESCCMCryptor : NSObject 14 | 15 | + (nullable NSData *)encrypt:(NSData *)data withKey:(NSData *)key iv:(NSData *)iv; 16 | + (nullable NSData *)encrypt:(NSData *)data withKey:(NSData *)key iv:(NSData *)iv 17 | tagLength:(size_t)tagLength adata:(nullable NSData *)adata; 18 | 19 | @end 20 | 21 | NS_ASSUME_NONNULL_END 22 | -------------------------------------------------------------------------------- /AdyenCSE/ADYAESCCMCryptor.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017 Adyen B.V. 3 | // 4 | // This file is open source and available under the MIT license. See the LICENSE file for more info. 5 | // 6 | 7 | #import "ADYAESCCMCryptor.h" 8 | #import 9 | 10 | @implementation ADYAESCCMCryptor 11 | 12 | #if ADYC_AESCCM_TraceLog 13 | NSData *dh(unsigned char *d) { 14 | return [NSData dataWithBytes:d length:kCCBlockSizeAES128]; 15 | } 16 | #endif 17 | 18 | 19 | + (NSData *)encrypt:(NSData *)data withKey:(NSData *)key iv:(NSData *)iv 20 | { 21 | return [self encrypt:data withKey:key iv:iv tagLength:8]; 22 | } 23 | 24 | + (NSData *)encrypt:(NSData *)data withKey:(NSData *)key iv:(NSData *)iv tagLength:(size_t)tagLength { 25 | return [self encrypt:data withKey:key iv:iv tagLength:tagLength adata:nil]; 26 | } 27 | 28 | + (NSData *)encrypt:(NSData *)data withKey:(NSData *)key iv:(NSData *)iv 29 | tagLength:(size_t)tagLength adata:(NSData *)adata { 30 | NSParameterAssert(data); 31 | NSParameterAssert(key); 32 | NSParameterAssert(iv); 33 | 34 | NSMutableData *cipher = [NSMutableData dataWithBytes:data.bytes length:(data.length + tagLength)]; 35 | 36 | 37 | size_t LSize = 15 - iv.length; 38 | ccm_encrypt_message(key.bytes, key.length, 39 | tagLength, LSize, (unsigned char *)iv.bytes, 40 | (unsigned char *)cipher.mutableBytes, data.length, 41 | (unsigned char *)adata.bytes, adata.length); 42 | 43 | 44 | if (adata) { 45 | NSMutableData *fullCipher = [NSMutableData dataWithCapacity:(adata.length + cipher.length)]; 46 | [fullCipher appendData:adata]; 47 | [fullCipher appendData:cipher]; 48 | return fullCipher; 49 | } 50 | 51 | return cipher; 52 | } 53 | 54 | CCCryptorStatus aes_encrypt(const void *key, size_t kL, unsigned char *bytes, unsigned char *cipher) { 55 | size_t length = kCCBlockSizeAES128; 56 | size_t outLength; 57 | CCCryptorStatus result = CCCrypt(kCCEncrypt, 58 | kCCAlgorithmAES, 59 | kCCOptionECBMode, 60 | key, 61 | kL, 62 | NULL, 63 | bytes, 64 | length, 65 | cipher, 66 | length, 67 | &outLength); 68 | 69 | if (result != kCCSuccess) { 70 | NSLog(@"AES128 Encryption Error"); 71 | } 72 | return result; 73 | } 74 | 75 | #pragma mark - CCM 76 | 77 | #define CCM_BLOCKSIZE kCCBlockSizeAES128 78 | 79 | 80 | #define CCM_FLAGS(A,M,L) (((A > 0) << 6) | (((M - 2)/2) << 3) | (L - 1)) 81 | 82 | #define CCM_MASK_L(_L) ((1 << 8 * _L) - 1) 83 | 84 | #define CCM_SET_COUNTER(A,L,cnt,C) { \ 85 | int i; \ 86 | memset((A) + CCM_BLOCKSIZE - (L), 0, (L)); \ 87 | (C) = (cnt) & CCM_MASK_L(L); \ 88 | for (i = CCM_BLOCKSIZE - 1; (C) && (i > (L)); --i, (C) >>= 8) \ 89 | (A)[i] |= (C) & 0xFF; \ 90 | } 91 | 92 | 93 | // XORs `n` bytes byte-by-byte starting at `y` to the memory area starting at `x`. 94 | static inline void 95 | ccm_memxor(unsigned char *x, const unsigned char *y, size_t n) { 96 | while(n--) { 97 | *x ^= *y; 98 | x++; y++; 99 | } 100 | } 101 | 102 | static inline void 103 | ccm_block0(size_t M, /* number of auth bytes */ 104 | size_t L, /* number of bytes to encode message length */ 105 | size_t la, /* l(a) octets additional authenticated data */ 106 | size_t lm, /* l(m) message length */ 107 | unsigned char nonce[CCM_BLOCKSIZE], 108 | unsigned char *result) { 109 | int i; 110 | 111 | result[0] = CCM_FLAGS(la, M, L); 112 | 113 | /* copy the nonce */ 114 | memcpy(result + 1, nonce, CCM_BLOCKSIZE - L); 115 | 116 | for (i=0; i < L; i++) { 117 | result[15-i] = lm & 0xff; 118 | lm >>= 8; 119 | } 120 | } 121 | 122 | static inline void 123 | ccm_encrypt_xor(const void *key, size_t kL, 124 | size_t L, unsigned long counter, 125 | unsigned char *msg, size_t len, 126 | unsigned char A[CCM_BLOCKSIZE], 127 | unsigned char S[CCM_BLOCKSIZE]) { 128 | 129 | static unsigned long counter_tmp; 130 | 131 | CCM_SET_COUNTER(A, L, counter, counter_tmp); 132 | aes_encrypt(key, kL, A, S); 133 | ccm_memxor(msg, S, len); 134 | } 135 | 136 | static inline void 137 | ccm_mac(const void *key, size_t kL, 138 | unsigned char *msg, size_t len, 139 | unsigned char B[CCM_BLOCKSIZE], 140 | unsigned char X[CCM_BLOCKSIZE]) { 141 | size_t i; 142 | 143 | for (i = 0; i < len; ++i) 144 | B[i] = X[i] ^ msg[i]; 145 | 146 | #if ADYC_AESCCM_TraceLog 147 | NSLog(@"ccm_mac: %@", dh(B)); 148 | #endif 149 | 150 | aes_encrypt(key, kL, B, X); 151 | 152 | #if ADYC_AESCCM_TraceLog 153 | NSLog(@"ccm_mac e: %@", dh(X)); 154 | #endif 155 | } 156 | 157 | #define dtls_int_to_uint16(Field,Value) do { \ 158 | *(unsigned char*)(Field) = ((Value) >> 8) & 0xff; \ 159 | *(((unsigned char*)(Field))+1) = ((Value) & 0xff); \ 160 | } while(0) 161 | 162 | /** 163 | * Creates the CBC-MAC for the additional authentication data that 164 | * is sent in cleartext. The result is written to `X`. 165 | * 166 | * @param key The AES key 167 | * @param kL The AES key length 168 | * @param msg The message starting with the additional authentication data. 169 | * @param la The number of additional authentication bytes in msg. 170 | * @param B The input buffer for crypto operations. When this function 171 | * is called, B must be initialized with B0 (the first 172 | * authentication block. 173 | * @param X The output buffer where the result of the CBC calculation 174 | * is placed. 175 | */ 176 | static void 177 | ccm_add_auth_data(const void *key, size_t kL, 178 | const unsigned char *msg, size_t la, 179 | unsigned char B[CCM_BLOCKSIZE], 180 | unsigned char X[CCM_BLOCKSIZE]) { 181 | size_t i,j; 182 | 183 | aes_encrypt(key, kL, B, X); 184 | memset(B, 0, CCM_BLOCKSIZE); 185 | 186 | if (!la) 187 | return; 188 | 189 | 190 | /* Here we are building for small devices and thus 191 | * anticipate that the number of additional authentication bytes 192 | * will not exceed 65280 bytes (0xFF00) and we can skip the 193 | * workarounds required for j=6 and j=10 on devices with a word size 194 | * of 32 bits or 64 bits, respectively. 195 | */ 196 | 197 | assert(la < 0xFF00); 198 | j = 2; 199 | dtls_int_to_uint16(B, la); 200 | 201 | i = MIN(CCM_BLOCKSIZE - j, la); 202 | memcpy(B + j, msg, i); 203 | la -= i; 204 | msg += i; 205 | 206 | ccm_memxor(B, X, CCM_BLOCKSIZE); 207 | 208 | aes_encrypt(key, kL, B, X); 209 | 210 | while (la > CCM_BLOCKSIZE) { 211 | for (i = 0; i < CCM_BLOCKSIZE; ++i) 212 | B[i] = X[i] ^ *msg++; 213 | la -= CCM_BLOCKSIZE; 214 | 215 | aes_encrypt(key, kL, B, X); 216 | } 217 | 218 | if (la) { 219 | memset(B, 0, CCM_BLOCKSIZE); 220 | memcpy(B, msg, la); 221 | ccm_memxor(B, X, CCM_BLOCKSIZE); 222 | 223 | aes_encrypt(key, kL, B, X); 224 | } 225 | } 226 | 227 | /** 228 | * Authenticates and encrypts a message using AES in CCM mode. Please 229 | * see also RFC 3610 for the meaning of M, L, lm and la. 230 | * 231 | * @param key The AES key 232 | * @param kL The AES key length 233 | * @param M The number of authentication octets. 234 | * @param L The number of bytes used to encode the message length. 235 | * @param nonce The nonce value to use. You must provide CCM_BLOCKSIZE 236 | * nonce octets, although only the first 16 - L are used. 237 | * @param msg The message to encrypt. The first la octets are additional 238 | * authentication data that will be cleartext. Note that the 239 | * encryption operation modifies the contents of msg and adds 240 | * M bytes MAC. Therefore, the buffer must be at least 241 | * lm + M bytes large. 242 | * @param lm The actual length of msg. 243 | * @param aad A pointer to the additional authentication data (can be NULL if 244 | * la is zero). 245 | * @param la The number of additional authentication octets (may be zero). 246 | * @return length 247 | */ 248 | size_t 249 | ccm_encrypt_message(const void *key, size_t kL, 250 | size_t M, size_t L, 251 | unsigned char nonce[CCM_BLOCKSIZE], 252 | unsigned char *msg, size_t lm, 253 | const unsigned char *aad, size_t la) { 254 | size_t i, len; 255 | unsigned long counter_tmp; 256 | unsigned long counter = 1; /// @bug does not work correctly on ia32 when lm >= 2^16 257 | unsigned char A[CCM_BLOCKSIZE]; /* A_i blocks for encryption input */ 258 | unsigned char B[CCM_BLOCKSIZE]; /* B_i blocks for CBC-MAC input */ 259 | unsigned char S[CCM_BLOCKSIZE]; /* S_i = encrypted A_i blocks */ 260 | unsigned char X[CCM_BLOCKSIZE]; /* X_i = encrypted B_i blocks */ 261 | 262 | len = lm; /* save original length */ 263 | /* create the initial authentication block B0 */ 264 | ccm_block0(M, L, la, lm, nonce, B); 265 | 266 | #if ADYC_AESCCM_TraceLog 267 | NSLog(@"ccm_block0: %@", dh(B)); 268 | #endif 269 | 270 | //aes_encrypt(key, kL, B, X); 271 | //memset(B, 0, CCM_BLOCKSIZE); 272 | ccm_add_auth_data(key, kL, aad, la, B, X); 273 | 274 | #if ADYC_AESCCM_TraceLog 275 | NSLog(@"ccm_add_auth_data: B: %@ X: %@", dh(B), dh(X)); 276 | #endif 277 | 278 | /* initialize block template */ 279 | A[0] = L-1; 280 | 281 | // copy the nonce 282 | memcpy(A + 1, nonce, CCM_BLOCKSIZE - L); 283 | 284 | while (lm >= CCM_BLOCKSIZE) { 285 | // calculate MAC 286 | ccm_mac(key, kL, msg, CCM_BLOCKSIZE, B, X); 287 | 288 | 289 | // encrypt 290 | ccm_encrypt_xor(key, kL, L, counter, msg, CCM_BLOCKSIZE, A, S); 291 | 292 | // update local pointers 293 | lm -= CCM_BLOCKSIZE; 294 | msg += CCM_BLOCKSIZE; 295 | counter++; 296 | } 297 | 298 | if (lm) { 299 | /* Calculate MAC. The remainder of B must be padded with zeroes, so 300 | * B is constructed to contain X ^ msg for the first lm bytes (done in 301 | * mac() and X ^ 0 for the remaining CCM_BLOCKSIZE - lm bytes 302 | * (i.e., we can use memcpy() here). 303 | */ 304 | memcpy(B + lm, X + lm, CCM_BLOCKSIZE - lm); 305 | ccm_mac(key, kL, msg, lm, B, X); 306 | 307 | // encrypt 308 | ccm_encrypt_xor(key, kL, L, counter, msg, lm, A, S); 309 | 310 | // update local pointers 311 | msg += lm; 312 | } 313 | 314 | // calculate S_0 315 | CCM_SET_COUNTER(A, L, 0, counter_tmp); 316 | aes_encrypt(key, kL, A, S); 317 | 318 | for (i = 0; i < M; ++i) 319 | *msg++ = X[i] ^ S[i]; 320 | 321 | return len + M; 322 | } 323 | 324 | 325 | 326 | @end 327 | -------------------------------------------------------------------------------- /AdyenCSE/ADYCard.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017 Adyen B.V. 3 | // 4 | // This file is open source and available under the MIT license. See the LICENSE file for more info. 5 | // 6 | 7 | #import 8 | 9 | NS_ASSUME_NONNULL_BEGIN 10 | 11 | @interface ADYCard : NSObject 12 | 13 | @property (nonatomic, strong, nullable) NSDate *generationtime; 14 | @property (nonatomic, strong, nullable) NSString *number; 15 | @property (nonatomic, strong, nullable) NSString *holderName; 16 | @property (nonatomic, strong, nullable) NSString *cvc; 17 | @property (nonatomic, strong, nullable) NSString *expiryMonth; 18 | @property (nonatomic, strong, nullable) NSString *expiryYear; 19 | 20 | + (nullable ADYCard *)decode:(NSData *)json error:(NSError * _Nullable *)error; 21 | - (nullable NSData *)encode; 22 | 23 | @end 24 | 25 | NS_ASSUME_NONNULL_END 26 | -------------------------------------------------------------------------------- /AdyenCSE/ADYCard.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017 Adyen B.V. 3 | // 4 | // This file is open source and available under the MIT license. See the LICENSE file for more info. 5 | // 6 | 7 | #import "ADYCard.h" 8 | 9 | @interface ADYCard () 10 | @property (readonly) NSDateFormatter* dateFormatter; 11 | @end 12 | 13 | @implementation ADYCard 14 | 15 | + (ADYCard *)decode:(NSData *)json error:(NSError **)error { 16 | id result = [NSJSONSerialization JSONObjectWithData:json options:0 error:error]; 17 | if (!result) { 18 | return nil; 19 | } else if (![result isKindOfClass:[NSDictionary class]]) { 20 | return nil; 21 | } else { 22 | NSDictionary* dict = (NSDictionary*)result; 23 | ADYCard* card = [[ADYCard alloc] init]; 24 | card.number = dict[@"number"]; 25 | card.holderName = dict[@"holderName"]; 26 | card.cvc = dict[@"cvc"]; 27 | card.expiryMonth = dict[@"expiryMonth"]; 28 | card.expiryYear = dict[@"expiryYear"]; 29 | 30 | return card; 31 | } 32 | } 33 | 34 | - (NSData *)encode { 35 | NSMutableDictionary *dict = [NSMutableDictionary dictionary]; 36 | 37 | for (NSString *key in @[@"number", @"holderName", @"cvc", @"expiryMonth", @"expiryYear"]) { 38 | if ([self valueForKey:key]) { 39 | dict[key] = [self valueForKey:key]; 40 | } 41 | } 42 | 43 | if (self.generationtime) { 44 | dict[@"generationtime"] = [self.dateFormatter stringFromDate:self.generationtime]; 45 | } 46 | 47 | return [NSJSONSerialization dataWithJSONObject:dict options:0 error:nil]; 48 | } 49 | 50 | - (NSDateFormatter *)dateFormatter { 51 | static dispatch_once_t once; 52 | static NSDateFormatter *instance; 53 | dispatch_once(&once, ^{ 54 | instance = [[NSDateFormatter alloc] init]; 55 | instance.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]; 56 | instance.calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian]; 57 | instance.timeZone = [NSTimeZone timeZoneWithName:@"UTC"]; 58 | instance.dateFormat = @"yyyy-MM-dd'T'HH:mm:ssZZZZZ"; 59 | }); 60 | return instance; 61 | } 62 | 63 | @end 64 | -------------------------------------------------------------------------------- /AdyenCSE/ADYCryptor.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017 Adyen B.V. 3 | // 4 | // This file is open source and available under the MIT license. See the LICENSE file for more info. 5 | // 6 | 7 | #import 8 | 9 | NS_ASSUME_NONNULL_BEGIN 10 | 11 | @interface ADYCryptor : NSObject 12 | 13 | /** 14 | * Sets encoded message prefix 15 | * 16 | * @param prefix Prefix string, default: "" 17 | */ 18 | + (void)setMsgPrefix:(nullable NSString *)prefix; 19 | 20 | /** 21 | * Sets encoded message separator 22 | * 23 | * @param separator Message separator, default: "$" 24 | */ 25 | + (void)setMsgSeparator:(nullable NSString *)separator; 26 | 27 | 28 | /** 29 | * Encrypts the data with AES-CBC using 30 | * generated AES256 session key and IV (12) 31 | * Encrypts the session key with RSA using 32 | * public key (using Keychain) 33 | * 34 | * @param data data to be encrypted 35 | * @param keyInHex Public key in Hex with format "Exponent|Modulus" 36 | * 37 | * @return Fully composed message in format: 38 | * - a prefix 39 | * - a separator 40 | * - RSA encrypted AES key, base64 encoded 41 | * - a separator 42 | * - a Payload of iv and cipherText, base64 encoded 43 | * 44 | * @see `setMsgPrefix:` 45 | * @see `setMsgSeparator:` 46 | */ 47 | + (nullable NSString *)encrypt:(NSData *)data publicKeyInHex:(NSString *)keyInHex; 48 | 49 | + (nullable NSData *)aesEncrypt:(NSData *)data withKey:(NSData *)key iv:(NSData *)iv; 50 | + (nullable NSData *)rsaEncrypt:(NSData *)data withKeyInHex:(NSString *)keyInHex; 51 | 52 | 53 | + (NSData *)dataFromHex:(NSString *)hex; 54 | + (nullable NSData *)sha1FromStringInHex:(NSString *)stringInHex; 55 | 56 | @end 57 | 58 | NS_ASSUME_NONNULL_END 59 | -------------------------------------------------------------------------------- /AdyenCSE/ADYCryptor.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017 Adyen B.V. 3 | // 4 | // This file is open source and available under the MIT license. See the LICENSE file for more info. 5 | // 6 | 7 | #import "ADYCryptor.h" 8 | #import "ADYAESCCMCryptor.h" 9 | #import "ADYRSACryptor.h" 10 | #import 11 | #import 12 | 13 | @implementation ADYCryptor 14 | 15 | static NSString* crypt_msg_prefix = @""; 16 | static NSString* crypt_msg_separator = @"$"; 17 | static NSUInteger crypt_ivLength = 12; 18 | 19 | + (void)setMsgPrefix:(NSString *)prefix { 20 | if (!prefix) { 21 | prefix = @""; 22 | } 23 | crypt_msg_prefix = prefix; 24 | } 25 | 26 | + (void)setMsgSeparator:(NSString *)separator { 27 | if (!separator) { 28 | separator = @"$"; 29 | } 30 | crypt_msg_separator = separator; 31 | } 32 | 33 | + (NSString *)encrypt:(NSData *)data publicKeyInHex:(NSString *)keyInHex { 34 | NSParameterAssert(data); 35 | NSParameterAssert(keyInHex); 36 | 37 | OSStatus status = noErr; 38 | 39 | // generate a unique AES key and (later) encrypt it with the public RSA key of the merchant 40 | NSMutableData *key = [NSMutableData dataWithLength:kCCKeySizeAES256]; 41 | status = SecRandomCopyBytes(NULL, kCCKeySizeAES256, key.mutableBytes); 42 | if (status != noErr) { 43 | return nil; 44 | } 45 | 46 | // generate a nonce 47 | NSMutableData *iv = [NSMutableData dataWithLength:crypt_ivLength]; 48 | status = SecRandomCopyBytes(NULL, crypt_ivLength, iv.mutableBytes); 49 | if (status != noErr) { 50 | return nil; 51 | } 52 | 53 | NSData *cipherText = [self aesEncrypt:data withKey:key iv:iv]; 54 | 55 | if (!cipherText) { 56 | return nil; 57 | } 58 | 59 | // format of the fully composed message: 60 | // - a prefix 61 | // - a separator 62 | // - RSA encrypted AES key, base64 encoded 63 | // - a separator 64 | // - a Payload of iv and cipherText, base64 encoded 65 | NSMutableData *payload = [NSMutableData data]; 66 | [payload appendData:iv]; 67 | [payload appendData:cipherText]; 68 | 69 | NSData *encryptedKey = [self rsaEncrypt:key withKeyInHex:keyInHex]; 70 | 71 | NSString *result = nil; 72 | 73 | NSString *prefix = (crypt_msg_prefix.length == 0) ? @"" : [crypt_msg_prefix stringByAppendingString:crypt_msg_separator]; 74 | 75 | if (encryptedKey) { 76 | result = [NSString stringWithFormat:@"%@%@%@%@", 77 | prefix, 78 | [encryptedKey base64EncodedStringWithOptions:0], 79 | crypt_msg_separator, 80 | [payload base64EncodedStringWithOptions:0]]; 81 | } 82 | 83 | return result; 84 | } 85 | 86 | #pragma mark - Wrappers 87 | + (NSData *)aesEncrypt:(NSData *)data withKey:(NSData *)key iv:(NSData *)iv { 88 | NSParameterAssert(data); 89 | NSParameterAssert(key); 90 | NSParameterAssert(iv); 91 | 92 | return [ADYAESCCMCryptor encrypt:data withKey:key iv:iv]; 93 | } 94 | 95 | + (NSData *)rsaEncrypt:(NSData *)data withKeyInHex:(NSString *)keyInHex { 96 | NSParameterAssert(data); 97 | NSParameterAssert(keyInHex); 98 | 99 | return [ADYRSACryptor encrypt:data withKeyInHex:keyInHex]; 100 | } 101 | 102 | #pragma mark - Helpers 103 | 104 | + (NSData *)dataFromHex:(NSString *)hex { 105 | NSParameterAssert(hex); 106 | 107 | hex = [hex stringByReplacingOccurrencesOfString:@" " withString:@""]; 108 | if (hex.length & 1) { 109 | hex = [@"0" stringByAppendingString:hex]; 110 | } 111 | NSMutableData *data = [[NSMutableData alloc] initWithCapacity:hex.length/2]; 112 | unsigned char whole_byte; 113 | char byte_chars[3] = {'\0','\0','\0'}; 114 | int i; 115 | for (i=0; i < [hex length]/2; i++) { 116 | byte_chars[0] = [hex characterAtIndex:i*2]; 117 | byte_chars[1] = [hex characterAtIndex:i*2+1]; 118 | whole_byte = strtol(byte_chars, NULL, 16); 119 | [data appendBytes:&whole_byte length:1]; 120 | } 121 | return data; 122 | } 123 | 124 | 125 | + (NSData *)sha1FromStringInHex:(NSString *)stringInHex { 126 | NSParameterAssert(stringInHex); 127 | 128 | NSMutableData *digest = [NSMutableData dataWithCapacity:CC_SHA1_DIGEST_LENGTH]; 129 | NSData *stringBytes = [stringInHex dataUsingEncoding:NSUTF8StringEncoding]; 130 | if (CC_SHA1(stringBytes.bytes, (CC_LONG)stringBytes.length, digest.mutableBytes)) { 131 | if (digest.length == 0) { 132 | // fallback if SHA1 failed 133 | return stringBytes; 134 | } 135 | return digest; 136 | } 137 | return nil; 138 | } 139 | 140 | 141 | @end 142 | -------------------------------------------------------------------------------- /AdyenCSE/ADYEncrypter.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017 Adyen B.V. 3 | // 4 | // This file is open source and available under the MIT license. See the LICENSE file for more info. 5 | // 6 | 7 | #import 8 | #import "ADYCryptor.h" 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @interface ADYEncrypter : ADYCryptor 13 | 14 | /** 15 | * Encrypts the data with AES-CBC using 16 | * generated AES256 session key and IV (12) 17 | * Encrypts the session key with RSA using 18 | * public key (using Keychain) 19 | * 20 | * @param data data to be encrypted 21 | * @param keyInHex Public key in Hex with format "Exponent|Modulus" 22 | * 23 | * @return Fully composed message in format: 24 | * - a prefix 25 | * - a separator 26 | * - RSA encrypted AES key, base64 encoded 27 | * - a separator 28 | * - a Payload of iv and cipherText, base64 encoded 29 | * 30 | * @see `setMsgPrefix:` 31 | * @see `setMsgSeparator:` 32 | */ 33 | + (nullable NSString *)encrypt:(NSData *)data publicKeyInHex:(NSString *)keyInHex; 34 | 35 | @end 36 | 37 | NS_ASSUME_NONNULL_END 38 | -------------------------------------------------------------------------------- /AdyenCSE/ADYEncrypter.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017 Adyen B.V. 3 | // 4 | // This file is open source and available under the MIT license. See the LICENSE file for more info. 5 | // 6 | 7 | #import "ADYEncrypter.h" 8 | 9 | @implementation ADYEncrypter 10 | 11 | + (void)initialize { 12 | [self setMsgPrefix:@"adyenan0_1_1"]; 13 | } 14 | 15 | + (NSString *)encrypt:(NSData *)data publicKeyInHex:(NSString *)keyInHex { 16 | return [super encrypt:data publicKeyInHex:keyInHex]; 17 | } 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /AdyenCSE/ADYRSACryptor.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017 Adyen B.V. 3 | // 4 | // This file is open source and available under the MIT license. See the LICENSE file for more info. 5 | // 6 | 7 | #import 8 | 9 | NS_ASSUME_NONNULL_BEGIN 10 | 11 | @interface ADYRSACryptor : NSObject 12 | 13 | + (nullable NSData *)encrypt:(NSData *)data withKeyInHex:(NSString *)keyInHex; 14 | 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /AdyenCSE/ADYRSACryptor.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017 Adyen B.V. 3 | // 4 | // This file is open source and available under the MIT license. See the LICENSE file for more info. 5 | // 6 | 7 | #import "ADYRSACryptor.h" 8 | #import 9 | #import "ADYCryptor.h" 10 | 11 | @import Security; 12 | 13 | @implementation ADYRSACryptor 14 | 15 | + (NSData *)encrypt:(NSData *)data withKeyInHex:(NSString *)keyInHex { 16 | NSParameterAssert(data); 17 | NSParameterAssert(keyInHex); 18 | 19 | NSString *fingerprint = [[ADYCryptor sha1FromStringInHex:keyInHex] base64EncodedStringWithOptions:0]; 20 | 21 | SecKeyRef publicKey = [self loadRSAPublicKeyRefWithAppTag:fingerprint]; 22 | if (!publicKey) { 23 | 24 | NSArray *tokens = [keyInHex componentsSeparatedByString:@"|"]; 25 | if (tokens.count != 2) { 26 | return nil; 27 | } 28 | 29 | NSData *exponent = [ADYCryptor dataFromHex:tokens[0]]; 30 | NSData *modulus = [ADYCryptor dataFromHex:tokens[1]]; 31 | 32 | if (!exponent || !modulus) { 33 | return nil; 34 | } 35 | 36 | NSLog(@"Adding PublicKey to Keystore with fingerprint: %@", fingerprint); 37 | 38 | NSData *pubKeyData = [self generateRSAPublicKeyWithModulus:modulus exponent:exponent]; 39 | [self saveRSAPublicKey:pubKeyData appTag:fingerprint overwrite:YES]; 40 | publicKey = [self loadRSAPublicKeyRefWithAppTag:fingerprint]; 41 | } 42 | 43 | if (!publicKey) { 44 | NSLog(@"Problem obtaining SecKeyRef"); 45 | return nil; 46 | } 47 | 48 | return [self encrypt:data RSAPublicKey:publicKey padding:kSecPaddingPKCS1]; 49 | } 50 | 51 | 52 | // https://github.com/meinside/iphonelib/blob/master/security/CryptoUtil.m 53 | 54 | + (NSData *)generateRSAPublicKeyWithModulus:(NSData*)modulus exponent:(NSData*)exponent 55 | { 56 | const uint8_t DEFAULT_EXPONENT[] = {0x01, 0x00, 0x01,}; //default: 65537 57 | const uint8_t UNSIGNED_FLAG_FOR_BYTE = 0x81; 58 | const uint8_t UNSIGNED_FLAG_FOR_BYTE2 = 0x82; 59 | const uint8_t UNSIGNED_FLAG_FOR_BIGNUM = 0x00; 60 | const uint8_t SEQUENCE_TAG = 0x30; 61 | const uint8_t INTEGER_TAG = 0x02; 62 | 63 | uint8_t* modulusBytes = (uint8_t*)[modulus bytes]; 64 | uint8_t* exponentBytes = (uint8_t*)(exponent == nil ? DEFAULT_EXPONENT : [exponent bytes]); 65 | 66 | //(1) calculate lengths 67 | //- length of modulus 68 | int lenMod = (int)[modulus length]; 69 | if(modulusBytes[0] >= 0x80) 70 | lenMod ++; //place for UNSIGNED_FLAG_FOR_BIGNUM 71 | int lenModHeader = 2 + (lenMod >= 0x80 ? 1 : 0) + (lenMod >= 0x0100 ? 1 : 0); 72 | //- length of exponent 73 | int lenExp = exponent == nil ? sizeof(DEFAULT_EXPONENT) : (int)[exponent length]; 74 | int lenExpHeader = 2; 75 | //- length of body 76 | int lenBody = lenModHeader + lenMod + lenExpHeader + lenExp; 77 | //- length of total 78 | int lenTotal = 2 + (lenBody >= 0x80 ? 1 : 0) + (lenBody >= 0x0100 ? 1 : 0) + lenBody; 79 | 80 | int index = 0; 81 | uint8_t* byteBuffer = malloc(sizeof(uint8_t) * lenTotal); 82 | memset(byteBuffer, 0x00, sizeof(uint8_t) * lenTotal); 83 | 84 | //(2) fill up byte buffer 85 | //- sequence tag 86 | byteBuffer[index ++] = SEQUENCE_TAG; 87 | //- total length 88 | if(lenBody >= 0x80) 89 | byteBuffer[index ++] = (lenBody >= 0x0100 ? UNSIGNED_FLAG_FOR_BYTE2 : UNSIGNED_FLAG_FOR_BYTE); 90 | if(lenBody >= 0x0100) 91 | { 92 | byteBuffer[index ++] = (uint8_t)(lenBody / 0x0100); 93 | byteBuffer[index ++] = lenBody % 0x0100; 94 | } 95 | else 96 | byteBuffer[index ++] = lenBody; 97 | //- integer tag 98 | byteBuffer[index ++] = INTEGER_TAG; 99 | //- modulus length 100 | if(lenMod >= 0x80) 101 | byteBuffer[index ++] = (lenMod >= 0x0100 ? UNSIGNED_FLAG_FOR_BYTE2 : UNSIGNED_FLAG_FOR_BYTE); 102 | if(lenMod >= 0x0100) 103 | { 104 | byteBuffer[index ++] = (int)(lenMod / 0x0100); 105 | byteBuffer[index ++] = lenMod % 0x0100; 106 | } 107 | else 108 | byteBuffer[index ++] = lenMod; 109 | //- modulus value 110 | if(modulusBytes[0] >= 0x80) 111 | byteBuffer[index ++] = UNSIGNED_FLAG_FOR_BIGNUM; 112 | memcpy(byteBuffer + index, modulusBytes, sizeof(uint8_t) * [modulus length]); 113 | index += [modulus length]; 114 | //- exponent length 115 | byteBuffer[index ++] = INTEGER_TAG; 116 | byteBuffer[index ++] = lenExp; 117 | //- exponent value 118 | memcpy(byteBuffer + index, exponentBytes, sizeof(uint8_t) * lenExp); 119 | index += lenExp; 120 | 121 | if(index != lenTotal) 122 | NSLog(@"lengths mismatch: index = %d, lenTotal = %d", index, lenTotal); 123 | 124 | NSMutableData* buffer = [NSMutableData dataWithBytes:byteBuffer length:lenTotal]; 125 | free(byteBuffer); 126 | 127 | return buffer; 128 | } 129 | 130 | + (BOOL)saveRSAPublicKey:(NSData*)publicKey appTag:(NSString *)appTag overwrite:(BOOL)overwrite 131 | { 132 | 133 | NSDictionary *query = nil; 134 | query = @{ 135 | (__bridge id)kSecClass: (__bridge id)kSecClassKey, 136 | (__bridge id)kSecAttrKeyType: (__bridge id)kSecAttrKeyTypeRSA, 137 | (__bridge id)kSecAttrKeyClass: (__bridge id)kSecAttrKeyClassPublic, 138 | (__bridge id)kSecAttrApplicationTag: [appTag dataUsingEncoding:NSUTF8StringEncoding], 139 | (__bridge id)kSecValueData: publicKey, 140 | (__bridge id)kSecReturnPersistentRef: @(YES) 141 | }; 142 | 143 | CFDataRef ref; 144 | OSStatus status = SecItemAdd((__bridge CFDictionaryRef)query, (CFTypeRef *)&ref); 145 | 146 | 147 | if (status == noErr) { 148 | return YES; 149 | } else if(status == errSecDuplicateItem && overwrite == YES) { 150 | return [self updateRSAPublicKey:publicKey appTag:appTag]; 151 | } else { 152 | NSLog(@"result = %d", (int)status); 153 | } 154 | 155 | return NO; 156 | } 157 | 158 | + (BOOL)updateRSAPublicKey:(NSData*)publicKey appTag:(NSString*)appTag 159 | { 160 | NSDictionary *query = nil; 161 | query = @{ 162 | (__bridge id)kSecClass: (__bridge id)kSecClassKey, 163 | (__bridge id)kSecAttrKeyType: (__bridge id)kSecAttrKeyTypeRSA, 164 | (__bridge id)kSecAttrKeyClass: (__bridge id)kSecAttrKeyClassPublic, 165 | (__bridge id)kSecAttrApplicationTag: [appTag dataUsingEncoding:NSUTF8StringEncoding] 166 | }; 167 | OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, NULL); //don't need public key ref 168 | 169 | if(status == noErr) 170 | { 171 | query = @{ 172 | (__bridge id)kSecClass: (__bridge id)kSecClassKey, 173 | (__bridge id)kSecAttrKeyType: (__bridge id)kSecAttrKeyTypeRSA, 174 | (__bridge id)kSecAttrKeyClass: (__bridge id)kSecAttrKeyClassPublic, 175 | (__bridge id)kSecAttrApplicationTag: [appTag dataUsingEncoding:NSUTF8StringEncoding] 176 | }; 177 | status = SecItemUpdate((__bridge CFDictionaryRef)query, 178 | (__bridge CFDictionaryRef)@{(__bridge id)kSecValueData: publicKey}); 179 | 180 | NSLog(@"result = %d", (int)status); 181 | 182 | return status == noErr; 183 | } else { 184 | NSLog(@"result = %d", (int)status); 185 | } 186 | return NO; 187 | } 188 | 189 | + (BOOL)deleteRSAPublicKeyWithAppTag:(NSString*)appTag 190 | { 191 | NSDictionary *query = nil; 192 | query = @{ 193 | (__bridge id)kSecClass: (__bridge id)kSecClassKey, 194 | (__bridge id)kSecAttrKeyType: (__bridge id)kSecAttrKeyTypeRSA, 195 | (__bridge id)kSecAttrKeyClass: (__bridge id)kSecAttrKeyClassPublic, 196 | (__bridge id)kSecAttrApplicationTag: [appTag dataUsingEncoding:NSUTF8StringEncoding] 197 | }; 198 | OSStatus status = SecItemDelete((__bridge CFDictionaryRef)query); 199 | 200 | if (status != noErr) NSLog(@"result = %d", (int)status); 201 | 202 | return status == noErr; 203 | } 204 | 205 | /* 206 | * returned value(SecKeyRef) should be released with CFRelease() function after use. 207 | * 208 | */ 209 | + (SecKeyRef)loadRSAPublicKeyRefWithAppTag:(NSString*)appTag 210 | { 211 | NSDictionary *query = nil; 212 | query = @{ 213 | (__bridge id)kSecClass: (__bridge id)kSecClassKey, 214 | (__bridge id)kSecAttrKeyType: (__bridge id)kSecAttrKeyTypeRSA, 215 | (__bridge id)kSecAttrKeyClass: (__bridge id)kSecAttrKeyClassPublic, 216 | (__bridge id)kSecAttrApplicationTag: [appTag dataUsingEncoding:NSUTF8StringEncoding], 217 | (__bridge id)kSecReturnRef: @(YES) 218 | }; 219 | 220 | SecKeyRef publicKeyRef; 221 | OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef*)&publicKeyRef); 222 | 223 | if (status == noErr) { 224 | return publicKeyRef; 225 | } else { 226 | NSLog(@"result = %d", (int)status); 227 | return NULL; 228 | } 229 | } 230 | 231 | /** 232 | * encrypt with RSA public key 233 | * 234 | * padding = kSecPaddingPKCS1 / kSecPaddingNone 235 | * 236 | */ 237 | + (NSData *)encrypt:(NSData *)original RSAPublicKey:(SecKeyRef)publicKey padding:(SecPadding)padding 238 | { 239 | @try 240 | { 241 | size_t encryptedLength = SecKeyGetBlockSize(publicKey); 242 | uint8_t encrypted[encryptedLength]; 243 | 244 | OSStatus status = SecKeyEncrypt(publicKey, 245 | padding, 246 | original.bytes, 247 | original.length, 248 | encrypted, 249 | &encryptedLength); 250 | if (status == noErr) { 251 | NSData *encryptedData = [[NSData alloc] initWithBytes:(const void*)encrypted length:encryptedLength]; 252 | return encryptedData; 253 | } else { 254 | NSLog(@"result = %d", (int)status); 255 | return nil; 256 | } 257 | } 258 | @catch (NSException * e) 259 | { 260 | //do nothing 261 | NSLog(@"exception: %@", [e reason]); 262 | } 263 | return nil; 264 | } 265 | 266 | @end 267 | -------------------------------------------------------------------------------- /AdyenCSE/AdyenCSE.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017 Adyen B.V. 3 | // 4 | // This file is open source and available under the MIT license. See the LICENSE file for more info. 5 | // 6 | 7 | #ifndef AdyenCSE_h 8 | #define AdyenCSE_h 9 | 10 | #import "ADYAESCCMCryptor.h" 11 | #import "ADYCard.h" 12 | #import "ADYCryptor.h" 13 | #import "ADYEncrypter.h" 14 | #import "ADYRSACryptor.h" 15 | #import "NSDictionary+AdyenUtil.h" 16 | #import "NSString+AdyenURLEncoding.h" 17 | #import "NSString+AdyenUtil.h" 18 | 19 | #endif /* AdyenCSE_h */ 20 | -------------------------------------------------------------------------------- /AdyenCSE/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.1.1 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /AdyenCSE/NSDictionary+AdyenUtil.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017 Adyen B.V. 3 | // 4 | // This file is open source and available under the MIT license. See the LICENSE file for more info. 5 | // 6 | 7 | #import 8 | 9 | NS_ASSUME_NONNULL_BEGIN 10 | 11 | @interface NSDictionary (AdyenUtil) 12 | 13 | - (NSString *)encodeFormData DEPRECATED_MSG_ATTRIBUTE("Use -ady_encodeFormData instead."); 14 | - (NSString *)ady_encodeFormData; 15 | 16 | @end 17 | 18 | NS_ASSUME_NONNULL_END 19 | -------------------------------------------------------------------------------- /AdyenCSE/NSDictionary+AdyenUtil.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017 Adyen B.V. 3 | // 4 | // This file is open source and available under the MIT license. See the LICENSE file for more info. 5 | // 6 | 7 | #import "NSDictionary+AdyenUtil.h" 8 | #import "NSString+AdyenUtil.h" 9 | 10 | @implementation NSDictionary (AdyenUtil) 11 | 12 | - (NSString *)encodeFormData { 13 | return [self ady_encodeFormData]; 14 | } 15 | 16 | - (NSString *)ady_encodeFormData { 17 | NSMutableString* s = [NSMutableString string]; 18 | [self enumerateKeysAndObjectsUsingBlock:^(NSString* key, NSString* value, BOOL *stop) { 19 | if(s.length) { 20 | [s appendString:@"&"]; 21 | } 22 | [s appendFormat:@"%@=%@", [key ady_URLEncodedString], [value ady_URLEncodedString]]; 23 | }]; 24 | return s; 25 | } 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /AdyenCSE/NSString+AdyenURLEncoding.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017 Adyen B.V. 3 | // 4 | // This file is open source and available under the MIT license. See the LICENSE file for more info. 5 | // 6 | 7 | #import 8 | 9 | NS_ASSUME_NONNULL_BEGIN 10 | 11 | @interface NSString (AdyenURLEncoding) 12 | 13 | - (nullable NSString *)urlEncodeUsingEncoding:(NSStringEncoding)encoding DEPRECATED_MSG_ATTRIBUTE("Use -ady_URLEncodedString instead."); 14 | 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /AdyenCSE/NSString+AdyenURLEncoding.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017 Adyen B.V. 3 | // 4 | // This file is open source and available under the MIT license. See the LICENSE file for more info. 5 | // 6 | 7 | #import "NSString+AdyenURLEncoding.h" 8 | #import "NSString+AdyenUtil.h" 9 | 10 | @implementation NSString (AdyenURLEncoding) 11 | 12 | -(NSString *)urlEncodeUsingEncoding:(NSStringEncoding)encoding { 13 | return [self URLEncodedString]; 14 | } 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /AdyenCSE/NSString+AdyenUtil.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017 Adyen B.V. 3 | // 4 | // This file is open source and available under the MIT license. See the LICENSE file for more info. 5 | // 6 | 7 | #import 8 | 9 | NS_ASSUME_NONNULL_BEGIN 10 | 11 | @interface NSString (AdyenUtil) 12 | 13 | - (BOOL)isHexString DEPRECATED_MSG_ATTRIBUTE("Use -ady_isHexString instead."); 14 | - (BOOL)ady_isHexString; 15 | 16 | - (nullable NSString *)URLEncodedString DEPRECATED_MSG_ATTRIBUTE("Use -ady_URLEncodedString instead."); 17 | - (nullable NSString *)ady_URLEncodedString; 18 | 19 | @end 20 | 21 | NS_ASSUME_NONNULL_END 22 | -------------------------------------------------------------------------------- /AdyenCSE/NSString+AdyenUtil.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017 Adyen B.V. 3 | // 4 | // This file is open source and available under the MIT license. See the LICENSE file for more info. 5 | // 6 | 7 | #import "NSString+AdyenUtil.h" 8 | 9 | @implementation NSString (AdyenUtil) 10 | 11 | - (BOOL)isHexString { 12 | return [self ady_isHexString]; 13 | } 14 | 15 | - (BOOL)ady_isHexString { 16 | for(NSUInteger l=0; l= '0' && c <= '9') || 27 | (c >= 'a' && c <= 'f') || 28 | (c >= 'A' && c <= 'F'); 29 | } 30 | 31 | - (NSString *)URLEncodedString { 32 | return [self ady_URLEncodedString]; 33 | } 34 | 35 | - (NSString *)ady_URLEncodedString { 36 | NSCharacterSet *allowedCharacters = [[NSCharacterSet characterSetWithCharactersInString:@"!*'();:@&=+$,/?%#[]"] invertedSet]; 37 | 38 | return [self stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacters]; 39 | } 40 | 41 | @end 42 | -------------------------------------------------------------------------------- /Example/Example.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 252EEBED1E3E36A3BDDB6BD3 /* Pods_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5625DBEE8DD44E4472DBCA3B /* Pods_Example.framework */; }; 11 | E20AD1C81EFBE0F00065B70E /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = E20AD1C71EFBE0F00065B70E /* main.m */; }; 12 | E20AD1D31EFBE0F00065B70E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E20AD1D21EFBE0F00065B70E /* Assets.xcassets */; }; 13 | E20AD1E31EFBE1960065B70E /* ADYAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = E20AD1DE1EFBE1960065B70E /* ADYAppDelegate.m */; }; 14 | E20AD1E41EFBE1960065B70E /* ADYViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E20AD1E01EFBE1960065B70E /* ADYViewController.m */; }; 15 | E20AD1E51EFBE1960065B70E /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E20AD1E11EFBE1960065B70E /* Launch Screen.storyboard */; }; 16 | E20AD1E61EFBE1960065B70E /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E20AD1E21EFBE1960065B70E /* Main.storyboard */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXFileReference section */ 20 | 5625DBEE8DD44E4472DBCA3B /* Pods_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 21 | 90406CD3F15CACAE1F3439A5 /* Pods-Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-Example/Pods-Example.release.xcconfig"; sourceTree = ""; }; 22 | BA620743E393C76A817C89C8 /* Pods-Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Example.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Example/Pods-Example.debug.xcconfig"; sourceTree = ""; }; 23 | E20AD1C31EFBE0F00065B70E /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 24 | E20AD1C71EFBE0F00065B70E /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 25 | E20AD1D21EFBE0F00065B70E /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 26 | E20AD1D71EFBE0F00065B70E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 27 | E20AD1DD1EFBE1960065B70E /* ADYAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ADYAppDelegate.h; sourceTree = ""; }; 28 | E20AD1DE1EFBE1960065B70E /* ADYAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ADYAppDelegate.m; sourceTree = ""; }; 29 | E20AD1DF1EFBE1960065B70E /* ADYViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ADYViewController.h; sourceTree = ""; }; 30 | E20AD1E01EFBE1960065B70E /* ADYViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ADYViewController.m; sourceTree = ""; }; 31 | E20AD1E11EFBE1960065B70E /* Launch Screen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = "Launch Screen.storyboard"; sourceTree = ""; }; 32 | E20AD1E21EFBE1960065B70E /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = ""; }; 33 | /* End PBXFileReference section */ 34 | 35 | /* Begin PBXFrameworksBuildPhase section */ 36 | E20AD1C01EFBE0F00065B70E /* Frameworks */ = { 37 | isa = PBXFrameworksBuildPhase; 38 | buildActionMask = 2147483647; 39 | files = ( 40 | 252EEBED1E3E36A3BDDB6BD3 /* Pods_Example.framework in Frameworks */, 41 | ); 42 | runOnlyForDeploymentPostprocessing = 0; 43 | }; 44 | /* End PBXFrameworksBuildPhase section */ 45 | 46 | /* Begin PBXGroup section */ 47 | 5A6E9E59C43DFF8753865D7B /* Frameworks */ = { 48 | isa = PBXGroup; 49 | children = ( 50 | 5625DBEE8DD44E4472DBCA3B /* Pods_Example.framework */, 51 | ); 52 | name = Frameworks; 53 | sourceTree = ""; 54 | }; 55 | B8ABF4135E6E207DC351426B /* Pods */ = { 56 | isa = PBXGroup; 57 | children = ( 58 | BA620743E393C76A817C89C8 /* Pods-Example.debug.xcconfig */, 59 | 90406CD3F15CACAE1F3439A5 /* Pods-Example.release.xcconfig */, 60 | ); 61 | name = Pods; 62 | sourceTree = ""; 63 | }; 64 | E20AD1BA1EFBE0F00065B70E = { 65 | isa = PBXGroup; 66 | children = ( 67 | E20AD1C51EFBE0F00065B70E /* Example */, 68 | E20AD1C41EFBE0F00065B70E /* Products */, 69 | B8ABF4135E6E207DC351426B /* Pods */, 70 | 5A6E9E59C43DFF8753865D7B /* Frameworks */, 71 | ); 72 | sourceTree = ""; 73 | }; 74 | E20AD1C41EFBE0F00065B70E /* Products */ = { 75 | isa = PBXGroup; 76 | children = ( 77 | E20AD1C31EFBE0F00065B70E /* Example.app */, 78 | ); 79 | name = Products; 80 | sourceTree = ""; 81 | }; 82 | E20AD1C51EFBE0F00065B70E /* Example */ = { 83 | isa = PBXGroup; 84 | children = ( 85 | E20AD1DD1EFBE1960065B70E /* ADYAppDelegate.h */, 86 | E20AD1DE1EFBE1960065B70E /* ADYAppDelegate.m */, 87 | E20AD1DF1EFBE1960065B70E /* ADYViewController.h */, 88 | E20AD1E01EFBE1960065B70E /* ADYViewController.m */, 89 | E20AD1E11EFBE1960065B70E /* Launch Screen.storyboard */, 90 | E20AD1E21EFBE1960065B70E /* Main.storyboard */, 91 | E20AD1D21EFBE0F00065B70E /* Assets.xcassets */, 92 | E20AD1D71EFBE0F00065B70E /* Info.plist */, 93 | E20AD1C61EFBE0F00065B70E /* Supporting Files */, 94 | ); 95 | path = Example; 96 | sourceTree = ""; 97 | }; 98 | E20AD1C61EFBE0F00065B70E /* Supporting Files */ = { 99 | isa = PBXGroup; 100 | children = ( 101 | E20AD1C71EFBE0F00065B70E /* main.m */, 102 | ); 103 | name = "Supporting Files"; 104 | sourceTree = ""; 105 | }; 106 | /* End PBXGroup section */ 107 | 108 | /* Begin PBXNativeTarget section */ 109 | E20AD1C21EFBE0F00065B70E /* Example */ = { 110 | isa = PBXNativeTarget; 111 | buildConfigurationList = E20AD1DA1EFBE0F00065B70E /* Build configuration list for PBXNativeTarget "Example" */; 112 | buildPhases = ( 113 | 7DDABD7FB59166CEDB58DFBD /* [CP] Check Pods Manifest.lock */, 114 | E20AD1BF1EFBE0F00065B70E /* Sources */, 115 | E20AD1C01EFBE0F00065B70E /* Frameworks */, 116 | E20AD1C11EFBE0F00065B70E /* Resources */, 117 | 581A953C449B94E3E9E457DF /* [CP] Embed Pods Frameworks */, 118 | 53C0CF2169959844EE5A58AA /* [CP] Copy Pods Resources */, 119 | ); 120 | buildRules = ( 121 | ); 122 | dependencies = ( 123 | ); 124 | name = Example; 125 | productName = Example; 126 | productReference = E20AD1C31EFBE0F00065B70E /* Example.app */; 127 | productType = "com.apple.product-type.application"; 128 | }; 129 | /* End PBXNativeTarget section */ 130 | 131 | /* Begin PBXProject section */ 132 | E20AD1BB1EFBE0F00065B70E /* Project object */ = { 133 | isa = PBXProject; 134 | attributes = { 135 | LastUpgradeCheck = 0900; 136 | ORGANIZATIONNAME = Adyen; 137 | TargetAttributes = { 138 | E20AD1C21EFBE0F00065B70E = { 139 | CreatedOnToolsVersion = 8.3.3; 140 | ProvisioningStyle = Automatic; 141 | }; 142 | }; 143 | }; 144 | buildConfigurationList = E20AD1BE1EFBE0F00065B70E /* Build configuration list for PBXProject "Example" */; 145 | compatibilityVersion = "Xcode 3.2"; 146 | developmentRegion = English; 147 | hasScannedForEncodings = 0; 148 | knownRegions = ( 149 | en, 150 | Base, 151 | ); 152 | mainGroup = E20AD1BA1EFBE0F00065B70E; 153 | productRefGroup = E20AD1C41EFBE0F00065B70E /* Products */; 154 | projectDirPath = ""; 155 | projectRoot = ""; 156 | targets = ( 157 | E20AD1C21EFBE0F00065B70E /* Example */, 158 | ); 159 | }; 160 | /* End PBXProject section */ 161 | 162 | /* Begin PBXResourcesBuildPhase section */ 163 | E20AD1C11EFBE0F00065B70E /* Resources */ = { 164 | isa = PBXResourcesBuildPhase; 165 | buildActionMask = 2147483647; 166 | files = ( 167 | E20AD1E61EFBE1960065B70E /* Main.storyboard in Resources */, 168 | E20AD1E51EFBE1960065B70E /* Launch Screen.storyboard in Resources */, 169 | E20AD1D31EFBE0F00065B70E /* Assets.xcassets in Resources */, 170 | ); 171 | runOnlyForDeploymentPostprocessing = 0; 172 | }; 173 | /* End PBXResourcesBuildPhase section */ 174 | 175 | /* Begin PBXShellScriptBuildPhase section */ 176 | 53C0CF2169959844EE5A58AA /* [CP] Copy Pods Resources */ = { 177 | isa = PBXShellScriptBuildPhase; 178 | buildActionMask = 2147483647; 179 | files = ( 180 | ); 181 | inputPaths = ( 182 | ); 183 | name = "[CP] Copy Pods Resources"; 184 | outputPaths = ( 185 | ); 186 | runOnlyForDeploymentPostprocessing = 0; 187 | shellPath = /bin/sh; 188 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Example/Pods-Example-resources.sh\"\n"; 189 | showEnvVarsInLog = 0; 190 | }; 191 | 581A953C449B94E3E9E457DF /* [CP] Embed Pods Frameworks */ = { 192 | isa = PBXShellScriptBuildPhase; 193 | buildActionMask = 2147483647; 194 | files = ( 195 | ); 196 | inputPaths = ( 197 | "${SRCROOT}/Pods/Target Support Files/Pods-Example/Pods-Example-frameworks.sh", 198 | "${BUILT_PRODUCTS_DIR}/AdyenCSE/AdyenCSE.framework", 199 | ); 200 | name = "[CP] Embed Pods Frameworks"; 201 | outputPaths = ( 202 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AdyenCSE.framework", 203 | ); 204 | runOnlyForDeploymentPostprocessing = 0; 205 | shellPath = /bin/sh; 206 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Example/Pods-Example-frameworks.sh\"\n"; 207 | showEnvVarsInLog = 0; 208 | }; 209 | 7DDABD7FB59166CEDB58DFBD /* [CP] Check Pods Manifest.lock */ = { 210 | isa = PBXShellScriptBuildPhase; 211 | buildActionMask = 2147483647; 212 | files = ( 213 | ); 214 | inputPaths = ( 215 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 216 | "${PODS_ROOT}/Manifest.lock", 217 | ); 218 | name = "[CP] Check Pods Manifest.lock"; 219 | outputPaths = ( 220 | "$(DERIVED_FILE_DIR)/Pods-Example-checkManifestLockResult.txt", 221 | ); 222 | runOnlyForDeploymentPostprocessing = 0; 223 | shellPath = /bin/sh; 224 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 225 | showEnvVarsInLog = 0; 226 | }; 227 | /* End PBXShellScriptBuildPhase section */ 228 | 229 | /* Begin PBXSourcesBuildPhase section */ 230 | E20AD1BF1EFBE0F00065B70E /* Sources */ = { 231 | isa = PBXSourcesBuildPhase; 232 | buildActionMask = 2147483647; 233 | files = ( 234 | E20AD1C81EFBE0F00065B70E /* main.m in Sources */, 235 | E20AD1E31EFBE1960065B70E /* ADYAppDelegate.m in Sources */, 236 | E20AD1E41EFBE1960065B70E /* ADYViewController.m in Sources */, 237 | ); 238 | runOnlyForDeploymentPostprocessing = 0; 239 | }; 240 | /* End PBXSourcesBuildPhase section */ 241 | 242 | /* Begin XCBuildConfiguration section */ 243 | E20AD1D81EFBE0F00065B70E /* Debug */ = { 244 | isa = XCBuildConfiguration; 245 | buildSettings = { 246 | ALWAYS_SEARCH_USER_PATHS = NO; 247 | CLANG_ANALYZER_NONNULL = YES; 248 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 249 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 250 | CLANG_CXX_LIBRARY = "libc++"; 251 | CLANG_ENABLE_MODULES = YES; 252 | CLANG_ENABLE_OBJC_ARC = YES; 253 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 254 | CLANG_WARN_BOOL_CONVERSION = YES; 255 | CLANG_WARN_COMMA = YES; 256 | CLANG_WARN_CONSTANT_CONVERSION = YES; 257 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 258 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 259 | CLANG_WARN_EMPTY_BODY = YES; 260 | CLANG_WARN_ENUM_CONVERSION = YES; 261 | CLANG_WARN_INFINITE_RECURSION = YES; 262 | CLANG_WARN_INT_CONVERSION = YES; 263 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 264 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 265 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 266 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 267 | CLANG_WARN_STRICT_PROTOTYPES = YES; 268 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 269 | CLANG_WARN_UNREACHABLE_CODE = YES; 270 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 271 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 272 | COPY_PHASE_STRIP = NO; 273 | DEBUG_INFORMATION_FORMAT = dwarf; 274 | ENABLE_STRICT_OBJC_MSGSEND = YES; 275 | ENABLE_TESTABILITY = YES; 276 | GCC_C_LANGUAGE_STANDARD = gnu99; 277 | GCC_DYNAMIC_NO_PIC = NO; 278 | GCC_NO_COMMON_BLOCKS = YES; 279 | GCC_OPTIMIZATION_LEVEL = 0; 280 | GCC_PREPROCESSOR_DEFINITIONS = ( 281 | "DEBUG=1", 282 | "$(inherited)", 283 | ); 284 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 285 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 286 | GCC_WARN_UNDECLARED_SELECTOR = YES; 287 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 288 | GCC_WARN_UNUSED_FUNCTION = YES; 289 | GCC_WARN_UNUSED_VARIABLE = YES; 290 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 291 | MTL_ENABLE_DEBUG_INFO = YES; 292 | ONLY_ACTIVE_ARCH = YES; 293 | SDKROOT = iphoneos; 294 | TARGETED_DEVICE_FAMILY = "1,2"; 295 | }; 296 | name = Debug; 297 | }; 298 | E20AD1D91EFBE0F00065B70E /* Release */ = { 299 | isa = XCBuildConfiguration; 300 | buildSettings = { 301 | ALWAYS_SEARCH_USER_PATHS = NO; 302 | CLANG_ANALYZER_NONNULL = YES; 303 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 304 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 305 | CLANG_CXX_LIBRARY = "libc++"; 306 | CLANG_ENABLE_MODULES = YES; 307 | CLANG_ENABLE_OBJC_ARC = YES; 308 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 309 | CLANG_WARN_BOOL_CONVERSION = YES; 310 | CLANG_WARN_COMMA = YES; 311 | CLANG_WARN_CONSTANT_CONVERSION = YES; 312 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 313 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 314 | CLANG_WARN_EMPTY_BODY = YES; 315 | CLANG_WARN_ENUM_CONVERSION = YES; 316 | CLANG_WARN_INFINITE_RECURSION = YES; 317 | CLANG_WARN_INT_CONVERSION = YES; 318 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 319 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 320 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 321 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 322 | CLANG_WARN_STRICT_PROTOTYPES = YES; 323 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 324 | CLANG_WARN_UNREACHABLE_CODE = YES; 325 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 326 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 327 | COPY_PHASE_STRIP = NO; 328 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 329 | ENABLE_NS_ASSERTIONS = NO; 330 | ENABLE_STRICT_OBJC_MSGSEND = YES; 331 | GCC_C_LANGUAGE_STANDARD = gnu99; 332 | GCC_NO_COMMON_BLOCKS = YES; 333 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 334 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 335 | GCC_WARN_UNDECLARED_SELECTOR = YES; 336 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 337 | GCC_WARN_UNUSED_FUNCTION = YES; 338 | GCC_WARN_UNUSED_VARIABLE = YES; 339 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 340 | MTL_ENABLE_DEBUG_INFO = NO; 341 | SDKROOT = iphoneos; 342 | TARGETED_DEVICE_FAMILY = "1,2"; 343 | VALIDATE_PRODUCT = YES; 344 | }; 345 | name = Release; 346 | }; 347 | E20AD1DB1EFBE0F00065B70E /* Debug */ = { 348 | isa = XCBuildConfiguration; 349 | baseConfigurationReference = BA620743E393C76A817C89C8 /* Pods-Example.debug.xcconfig */; 350 | buildSettings = { 351 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 352 | INFOPLIST_FILE = Example/Info.plist; 353 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 354 | PRODUCT_BUNDLE_IDENTIFIER = com.adyen.AdyenCSEExample; 355 | PRODUCT_NAME = "$(TARGET_NAME)"; 356 | }; 357 | name = Debug; 358 | }; 359 | E20AD1DC1EFBE0F00065B70E /* Release */ = { 360 | isa = XCBuildConfiguration; 361 | baseConfigurationReference = 90406CD3F15CACAE1F3439A5 /* Pods-Example.release.xcconfig */; 362 | buildSettings = { 363 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 364 | INFOPLIST_FILE = Example/Info.plist; 365 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 366 | PRODUCT_BUNDLE_IDENTIFIER = com.adyen.AdyenCSEExample; 367 | PRODUCT_NAME = "$(TARGET_NAME)"; 368 | }; 369 | name = Release; 370 | }; 371 | /* End XCBuildConfiguration section */ 372 | 373 | /* Begin XCConfigurationList section */ 374 | E20AD1BE1EFBE0F00065B70E /* Build configuration list for PBXProject "Example" */ = { 375 | isa = XCConfigurationList; 376 | buildConfigurations = ( 377 | E20AD1D81EFBE0F00065B70E /* Debug */, 378 | E20AD1D91EFBE0F00065B70E /* Release */, 379 | ); 380 | defaultConfigurationIsVisible = 0; 381 | defaultConfigurationName = Release; 382 | }; 383 | E20AD1DA1EFBE0F00065B70E /* Build configuration list for PBXNativeTarget "Example" */ = { 384 | isa = XCConfigurationList; 385 | buildConfigurations = ( 386 | E20AD1DB1EFBE0F00065B70E /* Debug */, 387 | E20AD1DC1EFBE0F00065B70E /* Release */, 388 | ); 389 | defaultConfigurationIsVisible = 0; 390 | defaultConfigurationName = Release; 391 | }; 392 | /* End XCConfigurationList section */ 393 | }; 394 | rootObject = E20AD1BB1EFBE0F00065B70E /* Project object */; 395 | } 396 | -------------------------------------------------------------------------------- /Example/Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/Example.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Example/Example/ADYAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017 Adyen B.V. 3 | // 4 | // This file is open source and available under the MIT license. See the LICENSE file for more info. 5 | // 6 | 7 | @import UIKit; 8 | 9 | @interface ADYAppDelegate : UIResponder 10 | 11 | @property (strong, nonatomic) UIWindow *window; 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Example/Example/ADYAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017 Adyen B.V. 3 | // 4 | // This file is open source and available under the MIT license. See the LICENSE file for more info. 5 | // 6 | 7 | #import "ADYAppDelegate.h" 8 | 9 | @implementation ADYAppDelegate 10 | 11 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 12 | { 13 | // Override point for customization after application launch. 14 | return YES; 15 | } 16 | 17 | - (void)applicationWillResignActive:(UIApplication *)application 18 | { 19 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 20 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 21 | } 22 | 23 | - (void)applicationDidEnterBackground:(UIApplication *)application 24 | { 25 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 26 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 27 | } 28 | 29 | - (void)applicationWillEnterForeground:(UIApplication *)application 30 | { 31 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 32 | } 33 | 34 | - (void)applicationDidBecomeActive:(UIApplication *)application 35 | { 36 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 37 | } 38 | 39 | - (void)applicationWillTerminate:(UIApplication *)application 40 | { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /Example/Example/ADYViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017 Adyen B.V. 3 | // 4 | // This file is open source and available under the MIT license. See the LICENSE file for more info. 5 | // 6 | 7 | @import UIKit; 8 | 9 | @interface ADYViewController : UIViewController 10 | 11 | @end 12 | -------------------------------------------------------------------------------- /Example/Example/ADYViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017 Adyen B.V. 3 | // 4 | // This file is open source and available under the MIT license. See the LICENSE file for more info. 5 | // 6 | 7 | #import "ADYViewController.h" 8 | 9 | #import "AdyenCSE/AdyenCSE.h" 10 | 11 | @interface ADYViewController () 12 | 13 | @property (strong, nonatomic) IBOutlet UITextField *nameTextField; 14 | @property (strong, nonatomic) IBOutlet UITextField *cardTextField; 15 | @property (strong, nonatomic) IBOutlet UITextField *monthTextField; 16 | @property (strong, nonatomic) IBOutlet UITextField *yearTextField; 17 | @property (strong, nonatomic) IBOutlet UITextField *securityCodeTextField; 18 | 19 | @property (strong, nonatomic) NSArray *inputFields; 20 | 21 | @end 22 | 23 | @implementation ADYViewController 24 | 25 | #error Set your public key. 26 | static NSString * publicKey = @""; 27 | 28 | #error Set the payment authorisation URL on your server. 29 | static NSString * merchantPaymentAuthoriseUrl = @""; 30 | 31 | - (void)viewDidLoad 32 | { 33 | [super viewDidLoad]; 34 | 35 | self.inputFields = @[self.nameTextField, 36 | self.cardTextField, 37 | self.monthTextField, 38 | self.yearTextField, 39 | self.securityCodeTextField]; 40 | } 41 | 42 | - (void)didReceiveMemoryWarning 43 | { 44 | [super didReceiveMemoryWarning]; 45 | // Dispose of any resources that can be recreated. 46 | } 47 | 48 | - (IBAction)payAction:(id)sender { 49 | [self hideKeyboard]; 50 | 51 | if ([self isCreditCardFormComplete] == NO) { 52 | [self presentIncompleteCreditCardFormAlert]; 53 | return; 54 | } 55 | 56 | // Encrypt card details. 57 | NSString *encryptedCardDetails = [self encryptedCardDetails]; 58 | 59 | // Submit encrypted card details to the merchant server. 60 | [self submitEncryptedCardDetails:encryptedCardDetails]; 61 | } 62 | 63 | - (void)hideKeyboard { 64 | [self.inputFields enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 65 | UITextField *textField = (UITextField *)obj; 66 | [textField resignFirstResponder]; 67 | }]; 68 | } 69 | 70 | #pragma mark - Encryption 71 | 72 | - (NSString *)encryptedCardDetails { 73 | // Create a card details object. 74 | ADYCard *card = [ADYCard new]; 75 | card.generationtime = [NSDate new]; 76 | card.holderName = self.nameTextField.text; 77 | card.number = [self.cardTextField.text stringByReplacingOccurrencesOfString:@" " withString:@""]; 78 | card.expiryMonth = self.monthTextField.text; 79 | card.expiryYear = self.yearTextField.text; 80 | card.cvc = self.securityCodeTextField.text; 81 | 82 | // Encrypt card details. 83 | NSData *cardData = [card encode]; 84 | NSString *encryptedDetails = [ADYEncrypter encrypt:cardData publicKeyInHex:publicKey]; 85 | 86 | return encryptedDetails; 87 | } 88 | 89 | #pragma mark - Merchant Server Submission 90 | 91 | - (void)submitEncryptedCardDetails:(NSString *)encryptedCardDetails { 92 | NSURL *url = [NSURL URLWithString:merchantPaymentAuthoriseUrl]; 93 | NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; 94 | request.HTTPMethod = @"POST"; 95 | 96 | // URL-encode encrypted card details parameter. 97 | NSString *body = [NSString stringWithFormat:@"encryptedCard=%@", 98 | [encryptedCardDetails ady_URLEncodedString]]; 99 | request.HTTPBody = [body dataUsingEncoding:NSUTF8StringEncoding]; 100 | 101 | NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; 102 | NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration]; 103 | 104 | NSURLSessionDataTask *task = nil; 105 | task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) 106 | { 107 | NSString *message = nil; 108 | if (error) { 109 | message = error.localizedDescription; 110 | } else { 111 | message = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; 112 | } 113 | 114 | dispatch_async(dispatch_get_main_queue(), ^{ 115 | [self presentMerchantServerResponseWithMessage:message]; 116 | }); 117 | }]; 118 | [task resume]; 119 | } 120 | 121 | - (void)presentMerchantServerResponseWithMessage:(NSString *)message { 122 | UIAlertController *alertController = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Merchant Server Response", nil) 123 | message:message 124 | preferredStyle:UIAlertControllerStyleAlert]; 125 | [alertController addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"OK", nil) 126 | style:UIAlertActionStyleDefault 127 | handler:^(UIAlertAction * _Nonnull action) { 128 | [self dismissViewControllerAnimated:YES completion:nil]; 129 | }]]; 130 | [self presentViewController:alertController animated:YES completion:nil]; 131 | } 132 | 133 | #pragma mark - Validation 134 | 135 | - (BOOL)isCreditCardFormComplete { 136 | __block BOOL isComplete = YES; 137 | [self.inputFields enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 138 | UITextField *textField = (UITextField *)obj; 139 | if (textField.text.length == 0) { 140 | isComplete = NO; 141 | *stop = YES; 142 | } 143 | }]; 144 | 145 | return isComplete; 146 | } 147 | 148 | - (void)presentIncompleteCreditCardFormAlert { 149 | UIAlertController *alertController = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Incomplete Credit Card Form", nil) 150 | message:NSLocalizedString(@"Please fill in all the fields.", nil) 151 | preferredStyle:UIAlertControllerStyleAlert]; 152 | [alertController addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"OK", nil) 153 | style:UIAlertActionStyleDefault 154 | handler:^(UIAlertAction * _Nonnull action) { 155 | [self dismissViewControllerAnimated:YES completion:nil]; 156 | }]]; 157 | [self presentViewController:alertController animated:YES completion:nil]; 158 | } 159 | 160 | @end 161 | -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /Example/Example/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /Example/Example/Launch Screen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 27 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /Example/Example/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 29 | 30 | 31 | 32 | 33 | 34 | 40 | 41 | 42 | 43 | 44 | 45 | 51 | 52 | 53 | 54 | 55 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | -------------------------------------------------------------------------------- /Example/Example/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2017 Adyen B.V. 3 | // 4 | // This file is open source and available under the MIT license. See the LICENSE file for more info. 5 | // 6 | 7 | #import 8 | #import "ADYAppDelegate.h" 9 | 10 | int main(int argc, char * argv[]) { 11 | @autoreleasepool { 12 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([ADYAppDelegate class])); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | use_frameworks! 2 | 3 | target 'Example' do 4 | pod 'AdyenCSE', :path => '../' 5 | end 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) Adyen B.V. (https://www.adyen.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Adyen CSE for iOS 2 | [![Build Status](https://travis-ci.org/Adyen/adyen-cse-ios.svg?branch=master)](https://travis-ci.org/Adyen/adyen-cse-ios) 3 | [![License](https://img.shields.io/cocoapods/l/AdyenCSE.svg?style=flat)](https://github.com/Adyen/adyen-cse-ios/blob/master/LICENSE) 4 | [![CocoaPods](https://img.shields.io/cocoapods/p/AdyenCSE.svg?style=flat)](http://cocoapods.org/pods/AdyenCSE) 5 | [![CocoaPods](https://img.shields.io/cocoapods/v/AdyenCSE.svg?style=flat)](http://cocoapods.org/pods/AdyenCSE) 6 | [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 7 | 8 | This repository contains Adyen's Client-Side Encryption (CSE) library for iOS. With CSE card data is encrypted on a client side (in this case the iOS device) before you submit it through your own server to the Adyen API. By using CSE you reduce your scope of [PCI compliance](https://en.wikipedia.org/wiki/Payment_Card_Industry_Data_Security_Standard), because no raw card data travels through your server. This repository can be leveraged as a starting point to integrate Adyen's payment functionality fully in-app. 9 | 10 | ## Requirements 11 | The AdyenCSE-iOS library is written in Objective-C and is compatible with apps supporting iOS 7.0 and up. Looking for the Android or web equivalent? We have the CSE library also available in Java ([AdyenCSE-Android](https://github.com/Adyen/AdyenCSE-Android)) and JavaScript ([AdyenCSE-web](https://github.com/Adyen/CSE-JS)). 12 | 13 | All our CSE libraries rely on you [setting up your own server](https://docs.adyen.com/developers/adyen-mobile-checkout#merchantserver) for communicating with the Adyen API. By using a server you ensure that API authentication credentials never get exposed. Please note that you need to have [signed up for an account at Adyen](https://www.adyen.com/signup) before you can send requests to the Adyen API. 14 | 15 | ## Example 16 | 17 | For your convenience this repository contains an example app that can be used as a reference while integrating. 18 | 19 | To run the example project, type in the terminal: 20 | 21 | ```ruby 22 | pod try AdyenCSE 23 | ``` 24 | 25 | ## Installation 26 | 27 | AdyenCSE is available through either [CocoaPods](http://cocoapods.org) or [Carthage](https://github.com/Carthage/Carthage). 28 | 29 | ### Cocoapods 30 | 31 | 1. Add `pod 'AdyenCSE'` to your `Podfile`. 32 | 2. Run `pod install`. 33 | 34 | ### Carthage 35 | 36 | 1. Add `github "adyen/adyen-cse-ios"` to your `Cartfile`. 37 | 2. Run `carthage update`. 38 | 3. Link the framework with your target as described in [Carthage Readme](https://github.com/Carthage/Carthage#adding-frameworks-to-an-application). 39 | 40 | ## Usage 41 | 42 | The code below illustrates how you can collect and encrypt card payment data. 43 | 44 | ```obj-c 45 | #import "AdyenCSE/AdyenCSE.h" 46 | 47 | // Set the public key. 48 | NSString *publicKey = @"10001|B243E873CB9220BAFE71..."; 49 | 50 | // Create a card object. 51 | ADYCard *card = [ADYCard new]; 52 | card.generationtime = [NSDate new]; 53 | card.number = @"55551..."; 54 | card.holderName = @"John A..."; 55 | card.cvc = @"737"; 56 | card.expiryMonth = @"08"; 57 | card.expiryYear = @"2018"; 58 | 59 | // Encrypt card data. 60 | NSData *cardData = [card encode]; 61 | NSString *encryptedCard = [ADYEncrypter encrypt:cardData publicKeyInHex:publicKey]; 62 | ``` 63 | 64 | Note that you'll have to URL encode the `encryptedCard` value before sending it from the app to your server, as the `encryptedCard` is generated by the CSE library and must be exactly the same as you send it from the server to the Adyen API. 65 | 66 | ```obj-c 67 | NSURL *url = [NSURL URLWithString:merchantPaymentAuthoriseUrl]; 68 | NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; 69 | request.HTTPMethod = @"POST"; 70 | 71 | NSString *body = [NSString stringWithFormat:@"encryptedCard=%@",[encryptedCardDetails ady_URLEncodedString]]; 72 | request.HTTPBody = [body dataUsingEncoding:NSUTF8StringEncoding]; 73 | ``` 74 | 75 | ## Next steps 76 | 77 | ### Server side 78 | 79 | Once you get encrypted payment information on your server, you should submit it through an API call to the corresponding [Adyen endpoint](https://docs.adyen.com/developers/easy-encryption#apiendpointsee). For example, the code example below demonstrates how to pass payment data in JSON format: 80 | 81 | ```json 82 | curl -u "ws@Company.YourCompany":"YourWsPassword" \ 83 | -H "Content-Type: application/json" \ 84 | -X POST \ 85 | --data \ 86 | '{ 87 | "additionalData": { 88 | "card.encrypted.json":"adyenjs_0_1_4p1$..." 89 | }, 90 | 91 | "amount" : { 92 | "value" : 2000, 93 | "currency" : "EUR" 94 | }, 95 | 96 | "reference" : ["YourPaymentReference"], 97 | "merchantAccount" : ["YourMerchantAccountName"] 98 | }'\ 99 | https://pal-test.adyen.com/pal/servlet/Payment/v18/authorise 100 | ``` 101 | 102 | 103 | ### Recurring payments 104 | 105 | If your business model requires to bill your customers on a recurring basis, you may enable recurring payments using the Adyen platform. In this case Adyen securely stores payment details when you make the first `authorisation` call, so that you no longer need to provide this data in the future. 106 | 107 | To do this, add the `recurring` field to the payment request you make from your server to the Adyen platform. For example, if you want to enable both [shopper-not-present](https://docs.adyen.com/developers/recurring-manual#recurringpayment) and [one-click](https://docs.adyen.com/developers/recurring-manual#oneclickcardpayment) recurring modes for a specific payment, add the following field to the API call above: 108 | 109 | ```json 110 | "recurring" : { 111 | "contract" : "RECURRING,ONECLICK" 112 | } 113 | ``` 114 | 115 | For more information on recurring payments, refer to the [Adyen documentation](https://docs.adyen.com/developers/recurring-manual). 116 | 117 | ### Notifications 118 | 119 | After you have developed your app, set up your merchant server and successfully performed your first [test payment](https://docs.adyen.com/developers/test-cards-manual) it's time to complete your integration by registering for Adyen's notification service. After each payment initiation we push a notification to your server with the authorisation response, so you can be sure whether you can start delivering your goods or services. 120 | 121 | To subscribe to and integrate with the notification service, please check our [notification manual](https://docs.adyen.com/developers/easy-encryption#notificationsee). 122 | 123 | ### Going live 124 | 125 | Successfully integrated with our notification service? Congratulations, now it's time to start accepting payments for real! Assuming that you've been using your Adyen test account and the Adyen API's test endpoints, you can now make use of your Adyen live account and Adyen API [live endpoints](https://docs.adyen.com/developers/easy-encryption#apiendpointsee). 126 | 127 | ## Questions? 128 | 129 | If you have any questions or suggestions, please contact your account manager or send your inquiry to support@adyen.com. 130 | 131 | ## License 132 | 133 | This repository is open-source and available under the [MIT license](https://en.wikipedia.org/wiki/MIT_License). See the LICENSE file for more information. 134 | 135 | --------------------------------------------------------------------------------