├── .circleci └── config.yml ├── .github └── workflows │ ├── build-test.yml │ ├── release.yml │ └── update-docs.yml ├── .gitignore ├── .gitmodules ├── .jazzy.yaml ├── .swiftlint.yml ├── Brewfile ├── Cartfile ├── Cartfile.private ├── Cartfile.resolved ├── Dangerfile ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── PAYJP.podspec ├── PAYJP.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ ├── PAYJP.xcscheme │ └── PAYJPTests.xcscheme ├── PAYJPFlutterCore.podspec ├── Package.resolved ├── Package.swift ├── README.md ├── Sources ├── APIClient.swift ├── Common │ ├── PAYNotificationKey.swift │ └── PermissionFetcher.swift ├── Core │ ├── CallbackQueue.swift │ └── PAYJPSDK.swift ├── Extensions │ ├── Bundle+PAYJP.swift │ ├── String+PAYJP.swift │ ├── StringProtocol+PAYJP.swift │ ├── UIApplication+PAYJP.swift │ ├── UIColor+PAYJP.swift │ ├── UIView+PAYJP.swift │ └── UIViewController+PAYJP.swift ├── Formatters │ ├── CardNumberFormatter.swift │ ├── CvcFormatter.swift │ └── ExpirationFormatter.swift ├── Info.plist ├── Log.swift ├── Networking │ ├── Core │ │ ├── APIError.swift │ │ ├── BaseRequest.swift │ │ ├── Client.swift │ │ ├── ClientInfo.swift │ │ ├── JSONDecodable.swift │ │ ├── JSONDecoder+PAYJP.swift │ │ ├── LocalError.swift │ │ ├── NSErrorConverter.swift │ │ ├── ParametersSerialization.swift │ │ └── Request.swift │ ├── Models │ │ ├── Card.swift │ │ ├── CardBrand.swift │ │ ├── GetAcceptedBrandsResponse.swift │ │ ├── PAYCommonResponse.swift │ │ ├── PAYErrorResponse.swift │ │ ├── ThreeDSecureStatus.swift │ │ └── Token.swift │ ├── Requests │ │ ├── CreateTokenForApplePayRequest.swift │ │ ├── CreateTokenRequest.swift │ │ ├── FinishTokenThreeDSecureRequest.swift │ │ ├── GetAcceptedBrands.swift │ │ └── GetTokenRequest.swift │ ├── Services │ │ ├── AccountsService.swift │ │ └── TokensService.swift │ ├── TokenOperationObserver.swift │ └── TokenOperationStatus.swift ├── ObjC │ ├── CardIOCardParams.m │ ├── CardIOProxy.m │ ├── PAYJP.m │ └── Public │ │ ├── CardIOCardParams.h │ │ ├── CardIOProxy.h │ │ └── PAYJP.h ├── Resources │ ├── Assets.xcassets │ │ ├── Contents.json │ │ ├── amex.imageset │ │ │ ├── Contents.json │ │ │ └── amex.pdf │ │ ├── diners.imageset │ │ │ ├── Contents.json │ │ │ └── diners.pdf │ │ ├── discover.imageset │ │ │ ├── Contents.json │ │ │ └── discover.pdf │ │ ├── icon_camera.imageset │ │ │ ├── Contents.json │ │ │ └── icon_camera.pdf │ │ ├── icon_card.imageset │ │ │ ├── Contents.json │ │ │ └── icon_card.pdf │ │ ├── icon_card_cvc_3.imageset │ │ │ ├── Contents.json │ │ │ └── icon_card_cvc_3.pdf │ │ ├── icon_card_cvc_4.imageset │ │ │ ├── Contents.json │ │ │ └── icon_card_cvc_4.pdf │ │ ├── jcb.imageset │ │ │ ├── Contents.json │ │ │ └── jcb.pdf │ │ ├── logo_amex.imageset │ │ │ ├── Contents.json │ │ │ └── logo_amex.pdf │ │ ├── logo_diners.imageset │ │ │ ├── Contents.json │ │ │ └── logo_diners.pdf │ │ ├── logo_discover.imageset │ │ │ ├── Contents.json │ │ │ └── logo_discover.pdf │ │ ├── logo_jcb.imageset │ │ │ ├── Contents.json │ │ │ └── logo_jcb.pdf │ │ ├── logo_master.imageset │ │ │ ├── Contents.json │ │ │ └── logo_master.pdf │ │ ├── logo_visa.imageset │ │ │ ├── Contents.json │ │ │ └── logo_visa.pdf │ │ ├── mastercard.imageset │ │ │ ├── Contents.json │ │ │ └── mastercard.pdf │ │ └── visa.imageset │ │ │ ├── Contents.json │ │ │ └── visa.pdf │ ├── Resource.bundle │ │ ├── en.lproj │ │ │ ├── Localizable.strings │ │ │ └── tdserror.html │ │ └── ja.lproj │ │ │ ├── Localizable.strings │ │ │ └── tdserror.html │ └── Views │ │ ├── BrandImageCell.xib │ │ ├── CardForm.storyboard │ │ ├── CardFormDisplayStyledView.xib │ │ ├── CardFormLabelStyledView.xib │ │ ├── CardFormTableStyledView.xib │ │ └── ErrorView.xib ├── Styles │ ├── Color.swift │ ├── Radius.swift │ └── Style.swift ├── ThreeDSecure │ ├── ThreeDSecureProcessHandler.swift │ ├── ThreeDSecureSFSafariViewControllerDriver.swift │ ├── ThreeDSecureURLConfiguration.swift │ ├── ThreeDSecureWebDriver.swift │ ├── ThreeDSecureWebViewController.swift │ └── ThreeDSecureWebViewControllerDriver.swift ├── Validators │ ├── CardBrandTransformer.swift │ ├── CardHolderValidator.swift │ ├── CardNumberValidator.swift │ ├── CvcValidator.swift │ ├── ExpirationExtractor.swift │ ├── ExpirationValidator.swift │ └── PublicKeyValidator.swift └── Views │ ├── ActionButton.swift │ ├── BorderView.swift │ ├── BrandImageCell.swift │ ├── CardFormDisplayStyledView.swift │ ├── CardFormInput.swift │ ├── CardFormLabelStyledView.swift │ ├── CardFormScreenPresenter.swift │ ├── CardFormTableStyledView.swift │ ├── CardFormView.swift │ ├── CardFormViewController.swift │ ├── CardFormViewControllerDelegate.swift │ ├── CardFormViewDelegate.swift │ ├── CardFormViewViewModel.swift │ ├── CardNumber.swift │ ├── ErrorTranslator.swift │ ├── ErrorView.swift │ ├── Expiration.swift │ ├── ExtraAttribute.swift │ ├── FormError.swift │ ├── FormStyle.swift │ ├── FormTextField.swift │ └── PresetPhoneNumberTextField.swift ├── Tests ├── APIClientTests.swift ├── APIErrorNSErrorTests.swift ├── CardFromTokenTests.swift ├── Core │ ├── PAYJPSDKTests.m │ └── PAYJPSDKTests.swift ├── Fixtures │ ├── cardBrands.json │ ├── error.json │ ├── invalid_apple_pay_token.json │ ├── paymentData.json │ └── token.json ├── Formatters │ ├── CardNumberFormatterTests.swift │ ├── CvcFormatterTests.swift │ └── ExpirationFormatterTests.swift ├── Info.plist ├── Networking │ ├── Core │ │ ├── ClientInfoTests.swift │ │ └── ClientTests.swift │ ├── Models │ │ ├── GetAcceptedBrandsResponseTests.swift │ │ ├── PAYErrorResponseTests.swift │ │ ├── ThreeDSecureStatusTests.swift │ │ └── TokenTests.swift │ ├── NSErrorConverterTests.swift │ └── Requests │ │ ├── CreateTokenForApplePayRequestTests.swift │ │ ├── CreateTokenRequestTests.swift │ │ ├── GetAcceptedBrandsTests.swift │ │ └── GetTokenRequestTests.swift ├── PAYJPTests-Bridging-Header.h ├── PAYJPTests.xctestplan ├── String+PAYJPTests.swift ├── StringProtocol+PAYJPTests.swift ├── StubPaymentToken.swift ├── TestFixture.swift ├── ThreeDSecure │ ├── Mock.swift │ ├── ThreeDSecureProcessHandlerTests.swift │ ├── ThreeDSecureSFSafariViewControllerDriverTests.swift │ ├── ThreeDSecureURLConfigurationTests.swift │ ├── ThreeDSecureWebViewControllerDriverTests.swift │ └── ThreeDSecureWebViewControllerTests.swift ├── Validators │ ├── CardBrandTransformerTests.swift │ ├── CardHolderValidatorTests.swift │ ├── CardNumberValidatorTests.swift │ ├── CvcValidatorTests.swift │ ├── ExpirationExtractorTests.swift │ ├── ExpirationValidatorTests.swift │ └── PublicKeyValidatorTests.swift └── Views │ ├── CardFormScreenPresenterTests.swift │ ├── CardFormViewModelTests.swift │ ├── ErrorTranslatorTests.swift │ └── Mock.swift ├── docs ├── Classes.html ├── Classes │ ├── APIClient.html │ ├── Card.html │ ├── CardFormDisplayStyledView.html │ ├── CardFormLabelStyledView.html │ ├── CardFormTableStyledView.html │ ├── CardFormView.html │ ├── CardFormViewController.html │ ├── ClientInfo.html │ ├── ExtraAttributeEmail.html │ ├── ExtraAttributePhone.html │ ├── FormStyle.html │ ├── PAYErrorResponse.html │ ├── PAYJPSDK.html │ ├── PAYNotificationKey.html │ ├── ThreeDSecureProcessHandler.html │ ├── ThreeDSecureSFSafariViewControllerDriver.html │ ├── ThreeDSecureURLConfiguration.html │ ├── ThreeDSecureWebViewController.html │ ├── ThreeDSecureWebViewControllerDriver.html │ └── Token.html ├── Enums.html ├── Enums │ ├── APIError.html │ ├── CardBrand.html │ ├── CardFormResult.html │ ├── CardFormViewType.html │ ├── LocalError.html │ ├── ThreeDSecureProcessStatus.html │ └── TokenOperationStatus.html ├── Extensions.html ├── Extensions │ ├── NSNotification.html │ ├── Notification.html │ └── Notification │ │ └── Name.html ├── Protocols.html ├── Protocols │ ├── CardFormAction.html │ ├── CardFormStylable.html │ ├── CardFormViewControllerDelegate.html │ ├── CardFormViewDelegate.html │ ├── PAYErrorResponseType.html │ ├── PAYJPSDKType.html │ ├── ThreeDSecureProcessHandlerDelegate.html │ ├── ThreeDSecureProcessHandlerType.html │ ├── ThreeDSecureWebDriver.html │ ├── ThreeDSecureWebDriverDelegate.html │ └── TokenOperationObserverType.html ├── Typealiases.html ├── css │ ├── highlight.css │ └── jazzy.css ├── docsets │ └── PAYJP.docset │ │ └── Contents │ │ ├── Info.plist │ │ └── Resources │ │ ├── Documents │ │ ├── Classes.html │ │ ├── Classes │ │ │ ├── APIClient.html │ │ │ ├── Card.html │ │ │ ├── CardFormDisplayStyledView.html │ │ │ ├── CardFormLabelStyledView.html │ │ │ ├── CardFormTableStyledView.html │ │ │ ├── CardFormView.html │ │ │ ├── CardFormViewController.html │ │ │ ├── ClientInfo.html │ │ │ ├── ExtraAttributeEmail.html │ │ │ ├── ExtraAttributePhone.html │ │ │ ├── FormStyle.html │ │ │ ├── PAYErrorResponse.html │ │ │ ├── PAYJPSDK.html │ │ │ ├── PAYNotificationKey.html │ │ │ ├── ThreeDSecureProcessHandler.html │ │ │ ├── ThreeDSecureSFSafariViewControllerDriver.html │ │ │ ├── ThreeDSecureURLConfiguration.html │ │ │ ├── ThreeDSecureWebViewController.html │ │ │ ├── ThreeDSecureWebViewControllerDriver.html │ │ │ └── Token.html │ │ ├── Enums.html │ │ ├── Enums │ │ │ ├── APIError.html │ │ │ ├── CardBrand.html │ │ │ ├── CardFormResult.html │ │ │ ├── CardFormViewType.html │ │ │ ├── LocalError.html │ │ │ ├── ThreeDSecureProcessStatus.html │ │ │ └── TokenOperationStatus.html │ │ ├── Extensions.html │ │ ├── Extensions │ │ │ ├── NSNotification.html │ │ │ ├── Notification.html │ │ │ └── Notification │ │ │ │ └── Name.html │ │ ├── Protocols.html │ │ ├── Protocols │ │ │ ├── CardFormAction.html │ │ │ ├── CardFormStylable.html │ │ │ ├── CardFormViewControllerDelegate.html │ │ │ ├── CardFormViewDelegate.html │ │ │ ├── PAYErrorResponseType.html │ │ │ ├── PAYJPSDKType.html │ │ │ ├── ThreeDSecureProcessHandlerDelegate.html │ │ │ ├── ThreeDSecureProcessHandlerType.html │ │ │ ├── ThreeDSecureWebDriver.html │ │ │ ├── ThreeDSecureWebDriverDelegate.html │ │ │ └── TokenOperationObserverType.html │ │ ├── Typealiases.html │ │ ├── css │ │ │ ├── highlight.css │ │ │ └── jazzy.css │ │ ├── img │ │ │ ├── carat.png │ │ │ ├── dash.png │ │ │ ├── gh.png │ │ │ └── spinner.gif │ │ ├── index.html │ │ ├── js │ │ │ ├── jazzy.js │ │ │ ├── jazzy.search.js │ │ │ ├── jquery.min.js │ │ │ ├── lunr.min.js │ │ │ └── typeahead.jquery.js │ │ └── search.json │ │ └── docSet.dsidx ├── img │ ├── carat.png │ ├── dash.png │ ├── gh.png │ └── spinner.gif ├── index.html ├── js │ ├── jazzy.js │ ├── jazzy.search.js │ ├── jquery.min.js │ ├── lunr.min.js │ └── typeahead.jquery.js ├── search.json └── undocumented.json ├── example-objc ├── .gitignore ├── Podfile ├── example-objc.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── example-objc.xcscheme ├── example-objc.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── example-objc │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── CardFormViewExampleViewController.h │ ├── CardFormViewExampleViewController.m │ ├── CardFormViewScrollViewController.h │ ├── CardFormViewScrollViewController.m │ ├── ColorTheme.h │ ├── ExampleHostViewController.h │ ├── ExampleHostViewController.m │ ├── Info.plist │ ├── SampleService.h │ ├── SampleService.m │ ├── ThreeDSecureExampleViewController.h │ ├── ThreeDSecureExampleViewController.m │ ├── UIViewController+Alert.h │ ├── UIViewController+Alert.m │ ├── ViewController.h │ ├── ViewController.m │ ├── en.lproj │ └── Localizable.strings │ ├── ja.lproj │ └── Localizable.strings │ └── main.m ├── example-swift ├── .gitignore ├── Cartfile ├── example-swift-ui │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── Contents.json │ ├── Base.lproj │ │ └── LaunchScreen.storyboard │ ├── CardFormViewControllerExampleView.swift │ ├── ContentView.swift │ ├── Info.plist │ ├── Preview Content │ │ └── Preview Assets.xcassets │ │ │ └── Contents.json │ ├── SceneDelegate.swift │ └── ThreeDSecureProcessHandlerExampleView.swift ├── example-swift.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── example-swift │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── CardFormViewExampleViewController.swift │ ├── CardFormViewScrollViewController.swift │ ├── CardFormViewWith3DSViewController.swift │ ├── ColorTheme.swift │ ├── ExampleHostViewController.swift │ ├── Info.plist │ ├── SampleService.swift │ ├── ThreeDSecureExampleViewController.swift │ ├── ViewController.swift │ ├── en.lproj │ │ └── Localizable.strings │ └── ja.lproj │ │ └── Localizable.strings └── run-swiftlint.sh ├── fastlane ├── Fastfile ├── Pluginfile └── README.md ├── podspec.rb └── scripts ├── bash.source ├── carthage.sh ├── restore_signing_credential.sh ├── run-clang-format.sh └── run-swift-format.sh /.github/workflows/build-test.yml: -------------------------------------------------------------------------------- 1 | name: Build and Test 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | workflow_dispatch: 9 | 10 | env: 11 | CACHE_NUMBER: 0 12 | 13 | jobs: 14 | build: 15 | runs-on: macos-14 16 | timeout-minutes: 30 17 | steps: 18 | - uses: actions/checkout@v4 19 | - name: Install HomeBrew 20 | run: | 21 | brew bundle || true 22 | cat Brewfile.lock.json || true 23 | - run: xcodebuild -version 24 | - uses: actions/cache@v4 25 | with: 26 | path: vendor/bundle 27 | key: ${{ runner.os }}-gems-${{ env.CACHE_NUMBER }}-${{ hashFiles('**/Gemfile.lock') }} 28 | restore-keys: | 29 | ${{ runner.os }}-gems-${{ env.CACHE_NUMBER }}- 30 | - name: Install gems 31 | run: | 32 | bundle config set deployment 'true' 33 | bundle config set clean 'true' 34 | bundle install --jobs 4 --retry 3 35 | - uses: actions/cache@v4 36 | with: 37 | path: Carthage 38 | key: ${{ runner.os }}-carthage-${{ env.CACHE_NUMBER }}-${{ hashFiles('Cartfile.resolved') }} 39 | restore-keys: | 40 | ${{ runner.os }}-carthage-${{ env.CACHE_NUMBER }}- 41 | - run: bundle exec fastlane ios carthage_bootstrap 42 | - run: bundle exec fastlane ios check_swift_format 43 | - run: bundle exec fastlane ios check_objc_format 44 | - run: bundle exec fastlane ios test 45 | - run: bundle exec fastlane ios lint_podspec 46 | - run: bundle exec fastlane ios build_swiftpm 47 | - run: bundle exec fastlane ios build_carthage_swift_example 48 | - run: bundle exec fastlane ios build_cocoapods_objc_example 49 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | release: 9 | runs-on: macos-14 10 | timeout-minutes: 30 11 | steps: 12 | - uses: actions/checkout@v4 13 | - name: Install Gems 14 | run: | 15 | bundle config set deployment 'true' 16 | bundle config set clean 'true' 17 | bundle install --jobs 4 --retry 3 18 | - name: lint podspec 19 | run: bundle exec fastlane ios lint_podspec 20 | - name: publish to pod trunk 21 | run: | 22 | bundle exec pod trunk push PAYJP.podspec 23 | bundle exec pod trunk push PAYJPFlutterCore.podspec 24 | env: 25 | COCOAPODS_TRUNK_TOKEN: ${{ secrets.COCOAPODS_TRUNK_TOKEN }} 26 | -------------------------------------------------------------------------------- /.github/workflows/update-docs.yml: -------------------------------------------------------------------------------- 1 | name: Update Docs 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | workflow_dispatch: 8 | 9 | jobs: 10 | build: 11 | runs-on: macos-14 12 | timeout-minutes: 30 13 | steps: 14 | - uses: actions/checkout@v2 15 | - name: Install HomeBrew 16 | run: | 17 | brew bundle || true 18 | cat Brewfile.lock.json || true 19 | - run: xcodebuild -version 20 | - uses: actions/cache@v2 21 | with: 22 | path: vendor/bundle 23 | key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }} 24 | restore-keys: | 25 | ${{ runner.os }}-gems- 26 | - name: Install gems 27 | run: | 28 | bundle config set deployment 'true' 29 | bundle config set clean 'true' 30 | bundle install --jobs 4 --retry 3 31 | - run: bundle exec fastlane ios create_pr_to_update_docs 32 | env: 33 | GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### https://raw.github.com/github/gitignore/e287eac7617fc65e49cf6e9d61ba23ff64e5994e/swift.gitignore 2 | 3 | # Xcode 4 | # 5 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 6 | 7 | ## Build generated 8 | build/ 9 | DerivedData/ 10 | 11 | ## Various settings 12 | *.pbxuser 13 | !default.pbxuser 14 | *.mode1v3 15 | !default.mode1v3 16 | *.mode2v3 17 | !default.mode2v3 18 | *.perspectivev3 19 | !default.perspectivev3 20 | xcuserdata/ 21 | 22 | ## Other 23 | *.moved-aside 24 | *.xcuserstate 25 | *.xcscmblueprint 26 | *.xccheckout 27 | 28 | ## Obj-C/Swift specific 29 | *.hmap 30 | *.ipa 31 | *.dSYM.zip 32 | *.dSYM 33 | 34 | ## Playgrounds 35 | timeline.xctimeline 36 | playground.xcworkspace 37 | 38 | # Swift Package Manager 39 | # 40 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 41 | # Packages/ 42 | .build/ 43 | .swiftpm 44 | 45 | # CocoaPods 46 | # 47 | # We recommend against adding the Pods directory to your .gitignore. However 48 | # you should judge for yourself, the pros and cons are mentioned at: 49 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 50 | # 51 | # Pods/ 52 | 53 | # Carthage 54 | # 55 | 56 | Carthage/Checkouts 57 | Carthage/Build 58 | 59 | # fastlane 60 | # 61 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 62 | # screenshots whenever they are needed. 63 | # For more information about the recommended setup visit: 64 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 65 | 66 | fastlane/report.xml 67 | fastlane/Preview.html 68 | fastlane/screenshots 69 | fastlane/test_output 70 | 71 | 72 | vendor/bundle 73 | .bundle 74 | .ruby-version 75 | 76 | firebase-debug.log 77 | 78 | docs/docsets/*.tgz 79 | 80 | Brewfile.lock.json -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "Carthage/Checkouts/OHHTTPStubs"] 2 | path = Carthage/Checkouts/OHHTTPStubs 3 | url = https://github.com/AliSoftware/OHHTTPStubs.git 4 | -------------------------------------------------------------------------------- /.jazzy.yaml: -------------------------------------------------------------------------------- 1 | min_acl: public 2 | clean: true 3 | author: PAYJP 4 | author_url: https://pay.jp 5 | github_url: https://github.com/payjp/payjp-ios 6 | module: PAYJP 7 | swift_build_tool: spm 8 | hide_documentation_coverage: true 9 | copyright: "© 2024 [PAY.JP](https://pay.jp) All rights reserved." 10 | -------------------------------------------------------------------------------- /.swiftlint.yml: -------------------------------------------------------------------------------- 1 | excluded: 2 | - Carthage 3 | - example-objc 4 | - example-swift 5 | - vendor/bundle/ruby/ 6 | - .build 7 | disabled_rules: 8 | - xctfail_message 9 | - identifier_name 10 | - large_tuple 11 | # custom 12 | function_parameter_count: 6 13 | file_length: 500 14 | -------------------------------------------------------------------------------- /Brewfile: -------------------------------------------------------------------------------- 1 | brew "carthage" 2 | brew "swiftlint" 3 | brew "clang-format" 4 | -------------------------------------------------------------------------------- /Cartfile: -------------------------------------------------------------------------------- 1 | github "marmelroy/PhoneNumberKit" ~> 4.0 -------------------------------------------------------------------------------- /Cartfile.private: -------------------------------------------------------------------------------- 1 | github "AliSoftware/OHHTTPStubs" -------------------------------------------------------------------------------- /Cartfile.resolved: -------------------------------------------------------------------------------- 1 | github "AliSoftware/OHHTTPStubs" "9.1.0" 2 | github "marmelroy/PhoneNumberKit" "4.0.0" 3 | -------------------------------------------------------------------------------- /Dangerfile: -------------------------------------------------------------------------------- 1 | github.dismiss_out_of_range_messages 2 | 3 | warn("Big PR") if git.lines_of_code > 500 4 | warn("PR is classed as Work in Progress") if github.pr_title.include? "[WIP]" 5 | 6 | swiftlint.binary_path = '/usr/local/bin/swiftlint' 7 | swiftlint.lint_files inline_mode: true, fail_on_error: true -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gem "fastlane" 4 | gem "danger" 5 | gem "danger-swiftlint" 6 | gem "jazzy" 7 | gem "cocoapods" 8 | plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile') 9 | # workaround for https://github.com/dependabot/dependabot-core/issues/1720 10 | eval_gemfile('fastlane/Pluginfile') if File.exist?(plugins_path) 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2016-present PAY, Inc. (https://pay.co.jp/) 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 | -------------------------------------------------------------------------------- /PAYJP.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod lib lint PAYJP.podspec' to ensure this is a 3 | # valid spec before submitting. 4 | # 5 | # Any lines starting with a # are optional, but their use is encouraged 6 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html 7 | # 8 | require './podspec' 9 | 10 | Pod::Spec.new do |s| 11 | s.name = 'PAYJP' 12 | s.version = PAYJPSDK::VERSION 13 | s.summary = 'PAY.JP iOS SDK' 14 | s.description = 'PAY.JP iOS SDK https://pay.jp/docs' 15 | 16 | s.homepage = PAYJPSDK::HOMEPAGE_URL 17 | s.license = PAYJPSDK::LICENSE 18 | s.author = PAYJPSDK::AUTHOR 19 | s.source = PAYJPSDK::SOURCE 20 | s.module_name = PAYJPSDK::MODULE_NAME 21 | s.swift_versions = PAYJPSDK::SWIFT_VERSIONS 22 | 23 | s.ios.deployment_target = PAYJPSDK::IOS_DEPLOYMENT_TARGET 24 | 25 | s.source_files = PAYJPSDK::SOURCE_FILES 26 | s.resource_bundles = PAYJPSDK::RESOURCE_BUNDLES 27 | s.public_header_files = PAYJPSDK::PUBLIC_HEADER_FILES 28 | s.frameworks = PAYJPSDK::FRAMEWORKS 29 | 30 | s.pod_target_xcconfig = PAYJPSDK::POD_TARGET_XCCONFIG 31 | s.dependency 'PhoneNumberKit', '~> 4.0.0' 32 | end 33 | -------------------------------------------------------------------------------- /PAYJP.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /PAYJP.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /PAYJPFlutterCore.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod lib lint PAYJP.podspec' to ensure this is a 3 | # valid spec before submitting. 4 | # 5 | # Any lines starting with a # are optional, but their use is encouraged 6 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html 7 | # 8 | require './podspec' 9 | 10 | Pod::Spec.new do |s| 11 | s.name = 'PAYJPFlutterCore' 12 | s.version = PAYJPSDK::VERSION 13 | s.summary = 'PAY.JP iOS SDK for Flutter' 14 | s.description = 'PAY.JP iOS SDK https://pay.jp/docs' 15 | 16 | s.homepage = PAYJPSDK::HOMEPAGE_URL 17 | s.license = PAYJPSDK::LICENSE 18 | s.author = PAYJPSDK::AUTHOR 19 | s.source = PAYJPSDK::SOURCE 20 | s.module_name = PAYJPSDK::MODULE_NAME 21 | s.swift_versions = PAYJPSDK::SWIFT_VERSIONS 22 | 23 | s.ios.deployment_target = PAYJPSDK::IOS_DEPLOYMENT_TARGET 24 | 25 | s.source_files = PAYJPSDK::SOURCE_FILES 26 | s.resource_bundles = PAYJPSDK::RESOURCE_BUNDLES 27 | s.resources = PAYJPSDK::RESOURCES 28 | s.public_header_files = PAYJPSDK::PUBLIC_HEADER_FILES 29 | s.frameworks = PAYJPSDK::FRAMEWORKS 30 | 31 | s.pod_target_xcconfig = PAYJPSDK::POD_TARGET_XCCONFIG 32 | s.dependency 'PhoneNumberKit', '~> 4.0.0' 33 | 34 | end 35 | -------------------------------------------------------------------------------- /Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "pins": [ 4 | { 5 | "package": "PhoneNumberKit", 6 | "repositoryURL": "https://github.com/marmelroy/PhoneNumberKit.git", 7 | "state": { 8 | "branch": null, 9 | "revision": "28aee1bd9d4a8fa46304c598b73231b043161e63", 10 | "version": "4.0.0" 11 | } 12 | } 13 | ] 14 | }, 15 | "version": 1 16 | } 17 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.3 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "PAYJP", 6 | platforms: [ 7 | .iOS(.v10) 8 | ], 9 | products: [ 10 | .library(name: "PAYJP", targets: ["PAYJP"]) 11 | ], 12 | dependencies: [ 13 | .package(url: "https://github.com/marmelroy/PhoneNumberKit.git", from: "4.0.0") 14 | ], 15 | targets: [ 16 | .target( 17 | name: "PAYJP-ObjC", 18 | dependencies: [], 19 | path: "Sources/ObjC", 20 | publicHeadersPath: "Public" 21 | ), 22 | .target( 23 | name: "PAYJP", 24 | dependencies: [ 25 | "PAYJP-ObjC", 26 | .product(name: "PhoneNumberKit", package: "PhoneNumberKit") 27 | ], 28 | path: "Sources", 29 | exclude: [ 30 | "ObjC", 31 | "Info.plist" 32 | ], 33 | resources: [ 34 | .process("Resources/Views"), 35 | .process("Resources/Resource.bundle"), 36 | .copy("Resources/Assets.xcassets") 37 | ] 38 | ) 39 | ] 40 | ) 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PAY.JP iOS SDK 2 | 3 | [![CocoaPods](https://img.shields.io/cocoapods/v/PAYJP.svg)](https://github.com/payjp/payjp-ios) 4 | [![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 5 | [![Swift Package Manager compatible](https://img.shields.io/badge/Swift%20Package%20Manager-compatible-brightgreen.svg)](https://github.com/apple/swift-package-manager) 6 | ![Build and Test](https://github.com/payjp/payjp-ios/workflows/Build%20and%20Test/badge.svg?branch=master) 7 | 8 | オンライン決済サービス「[PAY.JP](https://pay.jp/)」のiOSアプリ組込み用のSDKです。 9 | 10 | 最新バージョンでは以下を実現するための機能が提供されています。 11 | 詳細はドキュメントを参照してください。 12 | 13 | - Apple Payアプリ内決済 14 | - https://pay.jp/docs/apple-pay 15 | - クレジットカードによる決済 16 | - https://pay.jp/docs/mobileapp-ios 17 | 18 | | `.tableStyled` | `.labelStyled` | `.displayStyled` | 19 | | - | - | - | 20 | | tableStyled | labelStyled | displayStyled | 21 | 22 | ## サンプルコード 23 | 24 | - Apple Pay: https://github.com/payjp/apple-pay-example 25 | - CreditCard (Swift, Carthage): https://github.com/payjp/payjp-ios/tree/master/example-swift 26 | - CreditCard (Objective-C, CocoaPods): https://github.com/payjp/payjp-ios/tree/master/example-objc 27 | 28 | ## 動作環境 29 | 30 | - Swift または Objective-C で開発された iOS アプリケーション 31 | - iOS 12.0以上 32 | - 最新安定版のXcode 33 | 34 | ## インストール 35 | 36 | [Carthage](https://github.com/Carthage/Carthage) でインストールする場合、以下のように記述してください 37 | 38 | ``` 39 | github "payjp/payjp-ios" 40 | ``` 41 | 42 | [CocoaPods](https://cocoapods.org) でもインストールすることができます。 43 | 44 | ```ruby 45 | pod 'PAYJP' 46 | ``` 47 | 48 | Xcode 12以上の場合、Swift Package Managerにも対応しています。 49 | 50 | ## SDK開発環境 51 | 52 | - Swift 5 53 | 54 | ## リファレンス 55 | 56 | - https://payjp.github.io/payjp-ios/ 57 | 58 | ## License 59 | 60 | PAY.JP iOS SDK is available under the MIT license. See the LICENSE file for more info. 61 | -------------------------------------------------------------------------------- /Sources/Common/PAYNotificationKey.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PAYNotificationKey.swift 3 | // PAYJP 4 | // 5 | // Created by 北川達也 on 2020/09/14. 6 | // Copyright © 2020 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class PAYNotificationKey: NSObject {} 12 | -------------------------------------------------------------------------------- /Sources/Common/PermissionFetcher.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PermissionFetcher.swift 3 | // PAYJP 4 | // 5 | // Created by Tadashi Wakayanagi on 2020/02/04. 6 | // Copyright © 2020 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import AVFoundation 11 | 12 | /// Request camera permission 13 | protocol PermissionFetcherType { 14 | /// Check permission authorization status 15 | func checkCamera() -> AVAuthorizationStatus 16 | /// Request permission 17 | func requestCamera(completion: @escaping () -> Void) 18 | } 19 | 20 | class PermissionFetcher: PermissionFetcherType { 21 | 22 | static let shared = PermissionFetcher() 23 | 24 | func checkCamera() -> AVAuthorizationStatus { 25 | return AVCaptureDevice.authorizationStatus(for: .video) 26 | } 27 | 28 | func requestCamera(completion: @escaping () -> Void) { 29 | AVCaptureDevice.requestAccess(for: AVMediaType.video, completionHandler: { (shouldAccess) in 30 | if shouldAccess { 31 | completion() 32 | } 33 | }) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Sources/Core/CallbackQueue.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CallbackQueue.swift 3 | // PAYJP 4 | // 5 | // Created by Li-Hsuan Chen on 2019/07/24. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | enum CallbackQueue { 12 | case main 13 | case current 14 | case operation(OperationQueue) 15 | case dispatch(DispatchQueue) 16 | 17 | public func execute(handler: @escaping () -> Void) { 18 | switch self { 19 | case .main: 20 | DispatchQueue.main.async { 21 | handler() 22 | } 23 | 24 | case .current: 25 | handler() 26 | 27 | case .operation(let operationQueue): 28 | operationQueue.addOperation { 29 | handler() 30 | } 31 | 32 | case .dispatch(let dispatchQueue): 33 | dispatchQueue.async { 34 | handler() 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Sources/Core/PAYJPSDK.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PAYJPSDK.swift 3 | // PAYJP 4 | // 5 | // Created by Li-Hsuan Chen on 2019/07/24. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | #if SWIFT_PACKAGE 11 | @_exported import PAYJP_ObjC 12 | #endif 13 | 14 | /// PAY.JP SDK initial settings. 15 | public protocol PAYJPSDKType: AnyObject { 16 | /// PAY.JP public key. 17 | static var publicKey: String? { get set } 18 | /// Locale. 19 | static var locale: Locale? { get set } 20 | /// 3DSecure URL configuration. 21 | static var threeDSecureURLConfiguration: ThreeDSecureURLConfiguration? { get set } 22 | } 23 | 24 | /// see PAYJPSDKType. 25 | @objc(PAYJPSDK) @objcMembers 26 | public final class PAYJPSDK: NSObject, PAYJPSDKType { 27 | 28 | private override init() {} 29 | 30 | // MARK: - PAYJPSDKType 31 | 32 | public static var publicKey: String? { 33 | didSet { 34 | guard let publicKey = publicKey else { 35 | authToken = "" 36 | return 37 | } 38 | // public key validation 39 | PublicKeyValidator.shared.validate(publicKey: publicKey) 40 | 41 | let data = "\(publicKey):".data(using: .utf8)! 42 | let base64String = data.base64EncodedString() 43 | authToken = "Basic \(base64String)" 44 | } 45 | } 46 | public static var locale: Locale? 47 | 48 | public static var threeDSecureURLConfiguration: ThreeDSecureURLConfiguration? 49 | 50 | public static var clientInfo: ClientInfo = .default 51 | 52 | // Update by Fastlane :bump_up_version 53 | public static let sdkVersion: String = "2.2.1" 54 | 55 | static var authToken: String = "" 56 | } 57 | -------------------------------------------------------------------------------- /Sources/Extensions/Bundle+PAYJP.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Bundle+PAYJP.swift 3 | // PAYJP 4 | // 5 | // Created by Tadashi Wakayanagi on 2020/01/20. 6 | // 7 | 8 | import Foundation 9 | 10 | extension Bundle { 11 | static let resourceBundle: Bundle = { 12 | guard let path = payjpBundle.path(forResource: "Resource", ofType: "bundle"), 13 | let bundle = Bundle(path: path) else { 14 | fatalError("Resource bundle cannot be found, please try to reinstall PAYJP SDK.") 15 | } 16 | return bundle 17 | }() 18 | 19 | #if PAYJPSDKCocoaPods 20 | static let payjpBundle: Bundle = { 21 | guard let path = frameworkBundle.path(forResource: "PAYJP", ofType: "bundle"), 22 | let bundle = Bundle(path: path) else { 23 | fatalError("PAYJP bundle cannot be found, please try to reinstall PAYJP SDK.") 24 | } 25 | return bundle 26 | }() 27 | #elseif SWIFT_PACKAGE 28 | static let payjpBundle: Bundle = .module 29 | #else 30 | static let payjpBundle: Bundle = .frameworkBundle 31 | #endif 32 | 33 | private static let frameworkBundle: Bundle = { 34 | return Bundle(for: PAYJPSDK.self) 35 | }() 36 | } 37 | -------------------------------------------------------------------------------- /Sources/Extensions/String+PAYJP.swift: -------------------------------------------------------------------------------- 1 | // 2 | // String+PAYJP.swift 3 | // PAYJP 4 | // 5 | // Created by Tadashi Wakayanagi on 2019/07/31. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | extension String { 13 | 14 | /// 画像を取得 15 | var image: UIImage? { 16 | return UIImage(named: self, in: .payjpBundle, compatibleWith: nil) 17 | } 18 | 19 | /// 多言語対応 20 | var localized: String { 21 | return NSLocalizedString(self, bundle: .resourceBundle, comment: "") 22 | } 23 | 24 | /// 数字かどうか 25 | var isDigitsOnly: Bool { 26 | return self.rangeOfCharacter(from: CharacterSet.decimalDigits.inverted) == nil 27 | } 28 | 29 | /// 文字列から数値のみを返す 30 | /// 31 | /// - Returns: 数値文字列 32 | func numberfy() -> String { 33 | let digitSet = CharacterSet.decimalDigits 34 | let filtered = String(self.unicodeScalars.filter { digitSet.contains($0) }) 35 | return filtered 36 | } 37 | 38 | /// 正規表現でキャプチャした文字列を抽出する 39 | /// 40 | /// - Parameters: 41 | /// - pattern: 正規表現 42 | /// - group: 抽出するグループ番号(>=1) 43 | /// - Returns: 抽出した文字列 44 | func capture(pattern: String, group: Int) -> String? { 45 | let result = capture(pattern: pattern, group: [group]) 46 | return result.isEmpty ? nil : result[0] 47 | } 48 | 49 | /// 正規表現でキャプチャした文字列を抽出する 50 | /// 51 | /// - Parameters: 52 | /// - pattern: 正規表現 53 | /// - group: 抽出するグループ番号(>=1)の配列 54 | /// - Returns: 抽出した文字列の配列 55 | func capture(pattern: String, group: [Int]) -> [String] { 56 | guard let regex = try? NSRegularExpression(pattern: pattern) else { 57 | return [] 58 | } 59 | 60 | guard let matched = regex.firstMatch(in: self, range: NSRange(location: 0, length: self.count)) else { 61 | return [] 62 | } 63 | 64 | return group.map { group -> String in 65 | return (self as NSString).substring(with: matched.range(at: group)) 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Sources/Extensions/StringProtocol+PAYJP.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StringProtocol+PAYJP.swift 3 | // PAYJP 4 | // 5 | // Created by Tadashi Wakayanagi on 2019/08/30. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension StringProtocol where Self: RangeReplaceableCollection { 12 | 13 | /// 区切り文字をカウント毎に挿入する 14 | /// 15 | /// - Parameters: 16 | /// - separator: 区切り文字 17 | /// - count: カウント 18 | mutating func insert(separator: Self, every count: Int) { 19 | for index in indices.reversed() where index != startIndex && 20 | distance(from: startIndex, to: index) % count == 0 { 21 | insert(contentsOf: separator, at: index) 22 | } 23 | } 24 | 25 | /// 区切り文字を指定したポジションに挿入する 26 | /// 27 | /// - Parameters: 28 | /// - separator: 区切り文字 29 | /// - positions: 挿入位置 30 | mutating func insert(separator: Self, positions: [Int]) { 31 | for index in indices.reversed() where index != startIndex && 32 | positions.contains(distance(from: startIndex, to: index)) { 33 | insert(contentsOf: separator, at: index) 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Sources/Extensions/UIApplication+PAYJP.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIApplication+PAYJP.swift 3 | // PAYJP 4 | // 5 | // Created by Tadashi Wakayanagi on 2020/03/30. 6 | // Copyright © 2020 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIApplication { 12 | class func topViewController(controller: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) 13 | -> UIViewController? { 14 | if let navigationController = controller as? UINavigationController { 15 | return topViewController(controller: navigationController.visibleViewController) 16 | } 17 | if let tabController = controller as? UITabBarController { 18 | if let selected = tabController.selectedViewController { 19 | return topViewController(controller: selected) 20 | } 21 | } 22 | if let presented = controller?.presentedViewController { 23 | return topViewController(controller: presented) 24 | } 25 | return controller 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Sources/Extensions/UIColor+PAYJP.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIColor+PAYJP.swift 3 | // PAYJP 4 | // 5 | // Created by TADASHI on 2019/09/20. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIColor { 12 | convenience init(hex: String, alpha: CGFloat) { 13 | let v = Int("000000" + hex, radix: 16) ?? 0 14 | let r = CGFloat(v / Int(powf(256, 2)) % 256) / 255 15 | let g = CGFloat(v / Int(powf(256, 1)) % 256) / 255 16 | let b = CGFloat(v / Int(powf(256, 0)) % 256) / 255 17 | self.init(red: r, green: g, blue: b, alpha: min(max(alpha, 0), 1)) 18 | } 19 | 20 | convenience init(hex: String) { 21 | self.init(hex: hex, alpha: 1.0) 22 | } 23 | 24 | var hexString: String { 25 | guard let components = self.cgColor.components else { return "000000" } 26 | let r = components[0] 27 | let g = components[1] 28 | let b = components[2] 29 | return String(format: "%02X%02X%02X", Int(r * 255), Int(g * 255), Int(b * 255)) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Sources/Extensions/UIView+PAYJP.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+PAYJP.swift 3 | // PAYJP 4 | // 5 | // Created by Li-Hsuan Chen on 2019/08/09. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIView { 12 | struct RectCorner: OptionSet { 13 | let rawValue: UInt 14 | 15 | static var minXMinYCorner = RectCorner(rawValue: 1 << 0) 16 | static var maxXMinYCorner = RectCorner(rawValue: 1 << 1) 17 | static var minXMaxYCorner = RectCorner(rawValue: 1 << 2) 18 | static var maxXMaxYCorner = RectCorner(rawValue: 1 << 3) 19 | static var allCorners: RectCorner = [.minXMinYCorner, 20 | .maxXMinYCorner, 21 | .minXMaxYCorner, 22 | .maxXMaxYCorner] 23 | } 24 | 25 | var parentViewController: UIViewController? { 26 | var parentResponder: UIResponder? = self 27 | while parentResponder != nil { 28 | parentResponder = parentResponder!.next 29 | if let viewController = parentResponder as? UIViewController { 30 | return viewController 31 | } 32 | } 33 | return nil 34 | } 35 | 36 | func roundingCorners(corners: RectCorner, radius: CGFloat) { 37 | if #available(iOS 11.0, *) { 38 | let corners = CACornerMask(rawValue: corners.rawValue) 39 | layer.cornerRadius = radius 40 | layer.maskedCorners = corners 41 | } else { 42 | let corners = UIRectCorner(rawValue: corners.rawValue) 43 | let path = UIBezierPath(roundedRect: bounds, 44 | byRoundingCorners: corners, 45 | cornerRadii: CGSize(width: radius, height: radius)) 46 | let mask = CAShapeLayer() 47 | mask.path = path.cgPath 48 | layer.mask = mask 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Sources/Extensions/UIViewController+PAYJP.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewController+PAYJP.swift 3 | // PAYJP 4 | // 5 | // Created by Tadashi Wakayanagi on 2019/11/25. 6 | // 7 | 8 | import UIKit 9 | 10 | extension UIViewController { 11 | func showError(message: String) { 12 | let alertController = UIAlertController(title: "エラー", message: message, preferredStyle: .alert) 13 | let close = UIAlertAction(title: "OK", style: .default, handler: nil) 14 | alertController.addAction(close) 15 | self.present(alertController, animated: true, completion: nil) 16 | } 17 | 18 | var isModal: Bool { 19 | if self.navigationController?.viewControllers.first != self { 20 | return false 21 | } 22 | if self.presentingViewController != nil { 23 | return true 24 | } 25 | if self.navigationController?.presentingViewController?.presentedViewController == self.navigationController { 26 | return true 27 | } 28 | if self.tabBarController?.presentingViewController is UITabBarController { 29 | return true 30 | } 31 | return false 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Sources/Formatters/CvcFormatter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CvcFormatter.swift 3 | // PAYJP 4 | // 5 | // Created by Tadashi Wakayanagi on 2019/07/19. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol CvcFormatterType { 12 | /// セキュリティコードをフォーマットする 13 | /// 14 | /// - Parameters: 15 | /// - cvc: セキュリティコード 16 | /// - brand: カードブランド 17 | /// - Returns: ブランド別でフォーマットしたcvc 18 | func string(from cvc: String?, brand: CardBrand) -> String? 19 | } 20 | 21 | struct CvcFormatter: CvcFormatterType { 22 | 23 | func string(from cvc: String?, brand: CardBrand) -> String? { 24 | if let cvc = cvc, !cvc.isEmpty { 25 | let filtered = cvc.numberfy() 26 | 27 | if filtered.isEmpty { return nil } 28 | 29 | let trimmed = String(filtered.unicodeScalars.prefix(brand.cvcLength)) 30 | return trimmed 31 | } 32 | return nil 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Sources/Formatters/ExpirationFormatter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExpirationFormatter.swift 3 | // PAYJP 4 | // 5 | // Created by Li-Hsuan Chen on 2019/07/16. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol ExpirationFormatterType { 12 | /// Returns a formatted value from given string. 13 | /// - parameter expiration: 変換したい文字列。 14 | /// - returns: MM/yy のフォーマットで返します、例: `01/20` 。変換できない場合は nil で返します。 15 | func string(from expiration: String?) -> Expiration? 16 | 17 | func string(month: Int?, year: Int?) -> String? 18 | } 19 | 20 | struct ExpirationFormatter: ExpirationFormatterType { 21 | func string(from expiration: String?) -> Expiration? { 22 | if let expiration = expiration, !expiration.isEmpty { 23 | var filtered = expiration.numberfy() 24 | 25 | if filtered.isEmpty { return nil } 26 | 27 | if let firstNumber = Int(String(filtered.prefix(1))) { 28 | // 0埋め 29 | if firstNumber > 1 { 30 | filtered = "0" + filtered 31 | } 32 | } 33 | 34 | filtered = String(filtered.unicodeScalars.prefix(4)) 35 | if filtered.count >= 3 { 36 | filtered.insert(separator: "/", every: 2) 37 | } 38 | 39 | let mmyy = "MM/YY" 40 | let start = mmyy.startIndex 41 | let end = mmyy.index(mmyy.startIndex, offsetBy: filtered.count, limitedBy: mmyy.endIndex) ?? mmyy.endIndex 42 | let masked = mmyy.replacingCharacters(in: start.. String? { 50 | guard let month = month else { return nil } 51 | 52 | let trimmedMonth = month % 100 53 | if !(1...12 ~= trimmedMonth) { return nil } 54 | 55 | var result = String(format: "%02d", trimmedMonth) + "/" 56 | 57 | if let year = year { 58 | result += String(format: "%02d", year % 100) 59 | } 60 | 61 | return result 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Sources/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 | 2.2.1 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Sources/Log.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Log.swift 3 | // PAYJP 4 | // 5 | // Created by Tadashi Wakayanagi on 2019/11/26. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// Debugビルド時のみログ出力する 12 | /// - Parameters: 13 | /// - debug: debug message 14 | /// - function: function name 15 | /// - file: file name 16 | /// - line: line number 17 | func print(debug: Any = "", function: String = #function, file: String = #file, line: Int = #line) { 18 | #if DEBUG 19 | var filename: NSString = file as NSString 20 | filename = filename.lastPathComponent as NSString 21 | Swift.print("File: \(filename), Line: \(line), Func: \(function) \n\(debug)") 22 | #endif 23 | } 24 | -------------------------------------------------------------------------------- /Sources/Networking/Core/BaseRequest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BaseRequest.swift 3 | // PAYJP 4 | // 5 | // Created by Li-Hsuan Chen on 2019/07/25. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol BaseRequest: Request {} 12 | 13 | extension BaseRequest { 14 | var baseUrl: URL { return URL(string: PAYJPApiEndpoint)! } 15 | var headerFields: [String: String] { 16 | var fields = [String: String]() 17 | fields["Authorization"] = PAYJPSDK.authToken 18 | fields["User-Agent"] = PAYJPSDK.clientInfo.userAgent 19 | fields["X-Payjp-Client-User-Agent"] = PAYJPSDK.clientInfo.json 20 | fields["Locale"] = PAYJPSDK.locale?.languageCode 21 | return fields 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Sources/Networking/Core/JSONDecodable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JSONDecodable.swift 3 | // PAYJP 4 | // 5 | // Created by Tatsuya Kitagawa on 2019/12/26. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol JSONDecodable { 12 | 13 | static func decodeJson(with data: Data, using decoder: JSONDecoder) throws -> Self 14 | } 15 | -------------------------------------------------------------------------------- /Sources/Networking/Core/JSONDecoder+PAYJP.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JSONDecoder+PAYJP.swift 3 | // PAYJP 4 | // 5 | // Created by Tatsuya Kitagawa on 2019/04/09. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension JSONDecoder { 12 | static var shared: JSONDecoder { 13 | let decoder = JSONDecoder() 14 | decoder.dateDecodingStrategy = .secondsSince1970 15 | return decoder 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Sources/Networking/Core/LocalError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BaseError.swift 3 | // PAYJP 4 | // 5 | // Created by Tadashi Wakayanagi on 2019/10/01. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// Local error types. 12 | public enum LocalError: LocalizedError, NSErrorSerializable { 13 | /// Invalid form input. 14 | case invalidFormInput 15 | 16 | // MARK: - LocalizedError 17 | 18 | public var errorDescription: String? { 19 | switch self { 20 | case .invalidFormInput: 21 | return "Form input data is invalid." 22 | } 23 | } 24 | 25 | public var errorCode: Int { 26 | switch self { 27 | case .invalidFormInput: 28 | return PAYErrorFormInvalid 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Sources/Networking/Core/NSErrorConverter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSErrorConverter.swift 3 | // PAYJP 4 | // 5 | // Created by Tadashi Wakayanagi on 2019/10/03. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol NSErrorConverterType { 12 | func convert(from error: Error) -> NSError? 13 | } 14 | 15 | struct NSErrorConverter: NSErrorConverterType { 16 | 17 | static let shared = NSErrorConverter() 18 | 19 | func convert(from error: Error) -> NSError? { 20 | if let error = error as? NSErrorSerializable { 21 | var baseUserInfo = [String: Any]() 22 | baseUserInfo[NSLocalizedDescriptionKey] = error.errorDescription ?? "Unknown error." 23 | let mergedUserInfo = baseUserInfo.merging(error.userInfo) { $1 } 24 | 25 | return NSError(domain: PAYErrorDomain, 26 | code: error.errorCode, 27 | userInfo: mergedUserInfo) 28 | } else { 29 | return NSError(domain: PAYErrorDomain, 30 | code: PAYErrorSystemError, 31 | userInfo: [NSLocalizedDescriptionKey: error.localizedDescription]) 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Sources/Networking/Core/ParametersSerialization.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ParametersSerialization.swift 3 | // PAYJP 4 | // 5 | // Created by Li-Hsuan Chen on 2019/07/24. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct ParametersSerialization { 12 | static func string(from parameters: [String: Any]) -> String { 13 | let pairs = parameters.map { key, value -> String in 14 | if value is NSNull { 15 | return key 16 | } 17 | 18 | let valueString = (value as? String) ?? "\(value)" 19 | return "\(escape(key))=\(escape(valueString))" 20 | } 21 | 22 | return pairs.joined(separator: "&") 23 | } 24 | 25 | private static func escape(_ string: String) -> String { 26 | return string.addingPercentEncoding(withAllowedCharacters: .payUrlQueryAllowed) ?? string 27 | } 28 | } 29 | 30 | extension CharacterSet { 31 | /// RFC 3986 Section 2.2, Section 3.4 32 | /// reserved = gen-delims / sub-delims 33 | /// gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@" 34 | /// sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "=" 35 | static let payUrlQueryAllowed: CharacterSet = { 36 | let generalDelimitersToEncode = ":#[]@" // Remove "?" and "/" due to RFC3986, Section 3.4 37 | let subDelimitersToEncode = "!$&'()*+,;=" 38 | let encodableDelimiters = CharacterSet(charactersIn: "\(generalDelimitersToEncode)\(subDelimitersToEncode)") 39 | return CharacterSet.urlQueryAllowed.subtracting(encodableDelimiters) 40 | }() 41 | } 42 | -------------------------------------------------------------------------------- /Sources/Networking/Models/GetAcceptedBrandsResponse.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GetAcceptedBrandsResponse.swift 3 | // PAYJP 4 | // 5 | // Created by Li-Hsuan Chen on 2019/07/26. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct GetAcceptedBrandsResponse: Decodable { 12 | /// 利用可能なブランドの配列 13 | let acceptedBrands: [CardBrand] 14 | /// livemodeかどうか 15 | let liveMode: Bool 16 | 17 | private enum CodingKeys: String, CodingKey { 18 | case acceptedBrands = "card_types_supported" 19 | case liveMode = "livemode" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Sources/Networking/Models/PAYCommonResponse.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PAYCommonResponse.swift 3 | // PAYJP 4 | // 5 | // Created by Tadashi Wakayanagi on 2020/04/01. 6 | // Copyright © 2020 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct PAYCommonResponse: Decodable { 12 | let object: String 13 | let id: String 14 | } 15 | -------------------------------------------------------------------------------- /Sources/Networking/Models/PAYErrorResponse.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PAYErrorResponse.swift 3 | // PAYJP 4 | // 5 | // Created by Li-Hsuan Chen on 2018/05/21. 6 | // 7 | 8 | import Foundation 9 | 10 | struct PAYErrorResult: Decodable { 11 | let error: PAYErrorResponse 12 | } 13 | 14 | /// PAY.JP API error response. 15 | @objc 16 | public protocol PAYErrorResponseType: NSObjectProtocol { 17 | /// The origin reponse's HTTP status code. 18 | var status: Int { get } 19 | /// The detail message of the error. 20 | var message: String? { get } 21 | /// The param that caused the error happend. 22 | var param: String? { get } 23 | /// The code of the error. Check https://pay.jp/docs/api/#error for more details. 24 | var code: String? { get } 25 | /// The type of the error. Check https://pay.jp/docs/api/#error for more details. 26 | var type: String? { get } 27 | } 28 | 29 | /// see PAYErrorResponseType. 30 | @objcMembers @objc 31 | public final class PAYErrorResponse: NSObject, PAYErrorResponseType, LocalizedError, Decodable { 32 | 33 | // MARK: - PAYErrorResponseType properties 34 | 35 | public let status: Int 36 | public let message: String? 37 | public let param: String? 38 | public let code: String? 39 | public let type: String? 40 | 41 | public override var description: String { 42 | // swiftlint:disable line_length 43 | return "status: \(status) message: \(message ?? "") param: \(param ?? "") code: \(code ?? "") type: \(type ?? "")" 44 | // swiftlint:enable line_length 45 | } 46 | 47 | public var errorDescription: String? { return description } 48 | } 49 | -------------------------------------------------------------------------------- /Sources/Networking/Models/ThreeDSecureStatus.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ThreeDSecureStatus.swift 3 | // PAYJP 4 | // 5 | // Created by Tadashi Wakayanagi on 2020/04/02. 6 | // 7 | 8 | import Foundation 9 | 10 | class ThreeDSecureStatus { 11 | 12 | static func find(rawValue: String) -> PAYThreeDSecureStatus? { 13 | switch rawValue { 14 | case PAYThreeDSecureStatus.unverified.rawValue: 15 | return PAYThreeDSecureStatus.unverified 16 | case PAYThreeDSecureStatus.verified.rawValue: 17 | return PAYThreeDSecureStatus.verified 18 | case PAYThreeDSecureStatus.failed.rawValue: 19 | return PAYThreeDSecureStatus.failed 20 | case PAYThreeDSecureStatus.attempted.rawValue: 21 | return PAYThreeDSecureStatus.attempted 22 | case PAYThreeDSecureStatus.aborted.rawValue: 23 | return PAYThreeDSecureStatus.aborted 24 | case PAYThreeDSecureStatus.error.rawValue: 25 | return PAYThreeDSecureStatus.error 26 | default: 27 | return nil 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Sources/Networking/Requests/CreateTokenForApplePayRequest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CreateTokenForApplePayRequest.swift 3 | // PAYJP 4 | // 5 | // Created by Li-Hsuan Chen on 2019/07/25. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct CreateTokenForApplePayRequest: BaseRequest { 12 | 13 | // MARK: - Request 14 | 15 | typealias Response = Token 16 | var path: String = "tokens" 17 | var httpMethod: String = "POST" 18 | var bodyParameters: [String: String]? { 19 | return ["card": paymentToken] 20 | } 21 | 22 | // MARK: - Data 23 | 24 | let paymentToken: String 25 | 26 | init(paymentToken: String) { 27 | self.paymentToken = paymentToken 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Sources/Networking/Requests/CreateTokenRequest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CreateTokenRequest.swift 3 | // PAYJP 4 | // 5 | // Created by Li-Hsuan Chen on 2019/07/25. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct CreateTokenRequest: BaseRequest { 12 | typealias Response = Token 13 | 14 | var path: String = "tokens" 15 | var httpMethod: String = "POST" 16 | var bodyParameters: [String: String]? { 17 | var parameters = [ 18 | "card[number]": cardNumber, 19 | "card[cvc]": cvc, 20 | "card[exp_month]": expirationMonth, 21 | "card[exp_year]": expirationYear 22 | ] 23 | 24 | parameters["card[name]"] = name 25 | parameters["tenant"] = tenantId 26 | parameters["card[email]"] = email 27 | parameters["card[phone]"] = phone 28 | parameters["three_d_secure"] = String(threeDSecure) 29 | return parameters 30 | } 31 | 32 | // MARK: - Data 33 | 34 | let cardNumber: String 35 | let cvc: String 36 | let expirationMonth: String 37 | let expirationYear: String 38 | let name: String? 39 | let tenantId: String? 40 | let email: String? 41 | let phone: String? 42 | let threeDSecure: Bool 43 | } 44 | -------------------------------------------------------------------------------- /Sources/Networking/Requests/FinishTokenThreeDSecureRequest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FinishTokenThreeDSecureRequest.swift 3 | // PAYJP 4 | // 5 | // Created by 北川達也 on 2020/12/22. 6 | // Copyright © 2020 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct FinishTokenThreeDSecureRequest: BaseRequest { 12 | typealias Response = Token 13 | 14 | var path: String { return "tokens/\(tokenId)/tds_finish" } 15 | var httpMethod: String = "POST" 16 | 17 | let tokenId: String 18 | 19 | init(tokenId: String) { 20 | self.tokenId = tokenId 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Sources/Networking/Requests/GetAcceptedBrands.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GetAcceptedBrands.swift 3 | // PAYJP 4 | // 5 | // Created by Li-Hsuan Chen on 2019/07/25. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct GetAcceptedBrands: BaseRequest { 12 | 13 | // MARK: - Request 14 | 15 | typealias Response = GetAcceptedBrandsResponse 16 | 17 | var path: String = "accounts/brands" 18 | var httpMethod: String = "GET" 19 | var queryParameters: [String: Any]? { 20 | if let tenantId = tenantId { 21 | return ["tenant": tenantId] 22 | } 23 | return nil 24 | } 25 | 26 | // MARK: - Data 27 | 28 | let tenantId: String? 29 | 30 | init(tenantId: String?) { 31 | self.tenantId = tenantId 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Sources/Networking/Requests/GetTokenRequest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GetTokenRequest.swift 3 | // PAYJP 4 | // 5 | // Created by Li-Hsuan Chen on 2019/07/25. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct GetTokenRequest: BaseRequest { 12 | typealias Response = Token 13 | 14 | var path: String { return "tokens/\(tokenId)" } 15 | var httpMethod: String = "GET" 16 | 17 | let tokenId: String 18 | 19 | init(tokenId: String) { 20 | self.tokenId = tokenId 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Sources/Networking/Services/AccountsService.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AccountsService.swift 3 | // PAYJP 4 | // 5 | // Created by Li-Hsuan Chen on 2019/07/26. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// CardBrands Result typealias. 12 | public typealias CardBrandsResult = (Result<[CardBrand], APIError>) -> Void 13 | 14 | protocol AccountsServiceType { 15 | @discardableResult 16 | func getAcceptedBrands( 17 | tenantId: String?, 18 | completion: CardBrandsResult? 19 | ) -> URLSessionDataTask? 20 | } 21 | 22 | struct AccountsService: AccountsServiceType { 23 | 24 | private let client: ClientType 25 | 26 | static let shared = AccountsService() 27 | 28 | init(client: ClientType = Client.shared) { 29 | self.client = client 30 | } 31 | 32 | func getAcceptedBrands( 33 | tenantId: String?, 34 | completion: CardBrandsResult? 35 | ) -> URLSessionDataTask? { 36 | let request = GetAcceptedBrands(tenantId: tenantId) 37 | return client.request(with: request) { result in 38 | switch result { 39 | case .success(let data): 40 | completion?(.success(data.acceptedBrands)) 41 | case .failure(let error): 42 | completion?(.failure(error)) 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Sources/Networking/TokenOperationStatus.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TokenOperationStatus.swift 3 | // PAYJP 4 | // 5 | // Created by 北川達也 on 2020/09/11. 6 | // Copyright © 2020 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | @objc(PAYTokenOperationStatus) public enum TokenOperationStatus: Int { 12 | case acceptable 13 | case running 14 | case throttled 15 | } 16 | -------------------------------------------------------------------------------- /Sources/ObjC/CardIOCardParams.m: -------------------------------------------------------------------------------- 1 | // 2 | // CardIOCardParams.m 3 | // PAYJP 4 | // 5 | // Created by Li-Hsuan Chen on 2019/09/10. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | #import "CardIOCardParams.h" 10 | 11 | @implementation CardIOCardParams 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Sources/ObjC/PAYJP.m: -------------------------------------------------------------------------------- 1 | // 2 | // PAYJP.m 3 | // PAYJP 4 | // 5 | // Created by Li-Hsuan Chen on 2018/05/22. 6 | // 7 | 8 | #import "PAYJP.h" 9 | 10 | NSString *const PAYJPApiEndpoint = @"https://api.pay.jp/v1/"; 11 | 12 | NSString *const PAYErrorDomain = @"PAYErrorDomain"; 13 | 14 | NSInteger const PAYErrorInvalidApplePayToken = 0; 15 | NSInteger const PAYErrorSystemError = 1; 16 | NSInteger const PAYErrorInvalidResponse = 2; 17 | NSInteger const PAYErrorServiceError = 3; 18 | NSInteger const PAYErrorInvalidJSON = 4; 19 | NSInteger const PAYErrorFormInvalid = 5; 20 | NSInteger const PAYErrorRateLimitExceeded = 7; 21 | 22 | NSString *const PAYErrorInvalidApplePayTokenObject = @"PAYErrorInvalidApplePayToken"; 23 | NSString *const PAYErrorSystemErrorObject = @"PAYErrorSystemErrorObject"; 24 | NSString *const PAYErrorInvalidResponseObject = @"PAYErrorInvalidResponseObject"; 25 | NSString *const PAYErrorServiceErrorObject = @"PAYErrorServiceErrorObject"; 26 | NSString *const PAYErrorInvalidJSONObject = @"PAYErrorInvalidJSONObject"; 27 | NSString *const PAYErrorInvalidJSONErrorObject = @"PAYErrorInvalidJSONErrorObject"; 28 | 29 | PAYThreeDSecureStatus PAYThreeDSecureStatusUnverified = @"unverified"; 30 | PAYThreeDSecureStatus PAYThreeDSecureStatusVerified = @"verified"; 31 | PAYThreeDSecureStatus PAYThreeDSecureStatusFailed = @"failed"; 32 | PAYThreeDSecureStatus PAYThreeDSecureStatusAttempted = @"attempted"; 33 | PAYThreeDSecureStatus PAYThreeDSecureStatusAborted = @"aborted"; 34 | PAYThreeDSecureStatus PAYThreeDSecureStatusError = @"error"; 35 | -------------------------------------------------------------------------------- /Sources/ObjC/Public/CardIOCardParams.h: -------------------------------------------------------------------------------- 1 | // 2 | // CardIOCardParams.h 3 | // PAYJP 4 | // 5 | // Created by Li-Hsuan Chen on 2019/09/10. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface CardIOCardParams : NSObject 14 | 15 | @property(nonatomic, copy, nullable) NSString *number; 16 | @property(nonatomic, nullable) NSNumber *expiryMonth; 17 | @property(nonatomic, nullable) NSNumber *expiryYear; 18 | @property(nonatomic, copy, nullable) NSString *cvc; 19 | 20 | @end 21 | 22 | NS_ASSUME_NONNULL_END 23 | -------------------------------------------------------------------------------- /Sources/ObjC/Public/CardIOProxy.h: -------------------------------------------------------------------------------- 1 | // 2 | // CardIOProxy.h 3 | // PAYJP 4 | // 5 | // Created by Li-Hsuan Chen on 2019/08/08. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | #if TARGET_OS_IOS 13 | #import 14 | 15 | NS_ASSUME_NONNULL_BEGIN 16 | 17 | @class CardIOProxy, CardIOCardParams; 18 | 19 | @protocol CardIOProxyDelegate 20 | - (void)cardIOProxy:(CardIOProxy *)proxy 21 | didFinishWithCardParams:(CardIOCardParams *)cardParams 22 | NS_SWIFT_NAME(cardIOProxy(_:didFinishWith:)); 23 | - (void)didCancelCardIOProxy:(CardIOProxy *)proxy NS_SWIFT_NAME(didCancel(in:)); 24 | @end 25 | 26 | @interface CardIOProxy : NSObject 27 | 28 | + (BOOL)isCardIOAvailable; 29 | + (BOOL)canReadCardWithCamera; 30 | - (instancetype)initWithDelegate:(id)delegate; 31 | - (void)presentCardIOFromViewController:(UIViewController *)viewController; 32 | 33 | @end 34 | 35 | NS_ASSUME_NONNULL_END 36 | #endif 37 | -------------------------------------------------------------------------------- /Sources/ObjC/Public/PAYJP.h: -------------------------------------------------------------------------------- 1 | // 2 | // PAYJP.h 3 | // PAYJP 4 | // 5 | 6 | #import 7 | #import "CardIOCardParams.h" 8 | #import "CardIOProxy.h" 9 | 10 | FOUNDATION_EXPORT double PAYJPVersionNumber; 11 | FOUNDATION_EXPORT const unsigned char PAYJPVersionString[]; 12 | 13 | /// PAY.JP API's endpoint. 14 | FOUNDATION_EXPORT NSString *const PAYJPApiEndpoint; 15 | 16 | /// PAY.JP SDK related error's main domain. 17 | FOUNDATION_EXPORT NSString *const PAYErrorDomain; 18 | 19 | /// The Apple Pay token is invalid. 20 | FOUNDATION_EXPORT NSInteger const PAYErrorInvalidApplePayToken; 21 | /// The system error. 22 | FOUNDATION_EXPORT NSInteger const PAYErrorSystemError; 23 | /// No body data or no response error. 24 | FOUNDATION_EXPORT NSInteger const PAYErrorInvalidResponse; 25 | /// The error came back from server side. 26 | FOUNDATION_EXPORT NSInteger const PAYErrorServiceError; 27 | /// Invalid JSON object. 28 | FOUNDATION_EXPORT NSInteger const PAYErrorInvalidJSON; 29 | /// Form validation error. 30 | FOUNDATION_EXPORT NSInteger const PAYErrorFormInvalid; 31 | /// Too many requests 32 | FOUNDATION_EXPORT NSInteger const PAYErrorRateLimitExceeded; 33 | 34 | /// Use this key name to get `PAYErrorInvalidApplePayToken` error's data which is stored in the 35 | /// `userInfo`. 36 | FOUNDATION_EXPORT NSString *const PAYErrorInvalidApplePayTokenObject; 37 | /// Use this key name to get `PAYErrorSystemError` error's data which is stored in the `userInfo`. 38 | FOUNDATION_EXPORT NSString *const PAYErrorSystemErrorObject; 39 | /// Use this key name to get `PAYErrorInvalidResponse` error's data which is stored in the 40 | /// `userInfo`. 41 | FOUNDATION_EXPORT NSString *const PAYErrorInvalidResponseObject; 42 | /// Use this key name to get `PAYErrorServiceError` error's data which is stored in the `userInfo`. 43 | FOUNDATION_EXPORT NSString *const PAYErrorServiceErrorObject; 44 | /// Use this key name to get `PAYErrorInvalidJSON` error's data which is stored in the `userInfo`. 45 | FOUNDATION_EXPORT NSString *const PAYErrorInvalidJSONObject; 46 | FOUNDATION_EXPORT NSString *const PAYErrorInvalidJSONErrorObject; 47 | 48 | /// 3DSecure status 49 | typedef NSString *const PAYThreeDSecureStatus NS_STRING_ENUM; 50 | FOUNDATION_EXPORT PAYThreeDSecureStatus PAYThreeDSecureStatusUnverified; 51 | FOUNDATION_EXPORT PAYThreeDSecureStatus PAYThreeDSecureStatusVerified; 52 | FOUNDATION_EXPORT PAYThreeDSecureStatus PAYThreeDSecureStatusFailed; 53 | FOUNDATION_EXPORT PAYThreeDSecureStatus PAYThreeDSecureStatusAttempted; 54 | FOUNDATION_EXPORT PAYThreeDSecureStatus PAYThreeDSecureStatusAborted; 55 | FOUNDATION_EXPORT PAYThreeDSecureStatus PAYThreeDSecureStatusError; 56 | -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/amex.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "amex.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/amex.imageset/amex.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/payjp/payjp-ios/d4e228ba0bfc6dd82b216342a57c03059043ace7/Sources/Resources/Assets.xcassets/amex.imageset/amex.pdf -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/diners.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "diners.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/diners.imageset/diners.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/payjp/payjp-ios/d4e228ba0bfc6dd82b216342a57c03059043ace7/Sources/Resources/Assets.xcassets/diners.imageset/diners.pdf -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/discover.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "discover.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/discover.imageset/discover.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/payjp/payjp-ios/d4e228ba0bfc6dd82b216342a57c03059043ace7/Sources/Resources/Assets.xcassets/discover.imageset/discover.pdf -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/icon_camera.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icon_camera.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/icon_camera.imageset/icon_camera.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/payjp/payjp-ios/d4e228ba0bfc6dd82b216342a57c03059043ace7/Sources/Resources/Assets.xcassets/icon_camera.imageset/icon_camera.pdf -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/icon_card.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icon_card.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/icon_card.imageset/icon_card.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/payjp/payjp-ios/d4e228ba0bfc6dd82b216342a57c03059043ace7/Sources/Resources/Assets.xcassets/icon_card.imageset/icon_card.pdf -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/icon_card_cvc_3.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icon_card_cvc_3.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/icon_card_cvc_3.imageset/icon_card_cvc_3.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/payjp/payjp-ios/d4e228ba0bfc6dd82b216342a57c03059043ace7/Sources/Resources/Assets.xcassets/icon_card_cvc_3.imageset/icon_card_cvc_3.pdf -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/icon_card_cvc_4.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icon_card_cvc_4.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/icon_card_cvc_4.imageset/icon_card_cvc_4.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/payjp/payjp-ios/d4e228ba0bfc6dd82b216342a57c03059043ace7/Sources/Resources/Assets.xcassets/icon_card_cvc_4.imageset/icon_card_cvc_4.pdf -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/jcb.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "jcb.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/jcb.imageset/jcb.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/payjp/payjp-ios/d4e228ba0bfc6dd82b216342a57c03059043ace7/Sources/Resources/Assets.xcassets/jcb.imageset/jcb.pdf -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/logo_amex.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "logo_amex.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/logo_amex.imageset/logo_amex.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/payjp/payjp-ios/d4e228ba0bfc6dd82b216342a57c03059043ace7/Sources/Resources/Assets.xcassets/logo_amex.imageset/logo_amex.pdf -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/logo_diners.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "logo_diners.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/logo_diners.imageset/logo_diners.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/payjp/payjp-ios/d4e228ba0bfc6dd82b216342a57c03059043ace7/Sources/Resources/Assets.xcassets/logo_diners.imageset/logo_diners.pdf -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/logo_discover.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "logo_discover.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/logo_discover.imageset/logo_discover.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/payjp/payjp-ios/d4e228ba0bfc6dd82b216342a57c03059043ace7/Sources/Resources/Assets.xcassets/logo_discover.imageset/logo_discover.pdf -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/logo_jcb.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "logo_jcb.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/logo_jcb.imageset/logo_jcb.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/payjp/payjp-ios/d4e228ba0bfc6dd82b216342a57c03059043ace7/Sources/Resources/Assets.xcassets/logo_jcb.imageset/logo_jcb.pdf -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/logo_master.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "logo_master.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/logo_master.imageset/logo_master.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/payjp/payjp-ios/d4e228ba0bfc6dd82b216342a57c03059043ace7/Sources/Resources/Assets.xcassets/logo_master.imageset/logo_master.pdf -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/logo_visa.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "logo_visa.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/logo_visa.imageset/logo_visa.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/payjp/payjp-ios/d4e228ba0bfc6dd82b216342a57c03059043ace7/Sources/Resources/Assets.xcassets/logo_visa.imageset/logo_visa.pdf -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/mastercard.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "mastercard.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/mastercard.imageset/mastercard.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/payjp/payjp-ios/d4e228ba0bfc6dd82b216342a57c03059043ace7/Sources/Resources/Assets.xcassets/mastercard.imageset/mastercard.pdf -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/visa.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "visa.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Sources/Resources/Assets.xcassets/visa.imageset/visa.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/payjp/payjp-ios/d4e228ba0bfc6dd82b216342a57c03059043ace7/Sources/Resources/Assets.xcassets/visa.imageset/visa.pdf -------------------------------------------------------------------------------- /Sources/Styles/Color.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Color.swift 3 | // PAYJP 4 | // 5 | // Created by Tadashi Wakayanagi on 2019/09/24. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension Style { 12 | final class Color { 13 | 14 | static var label: UIColor { 15 | if #available(iOS 13.0, *) { 16 | return .label 17 | } else { 18 | return .init(hex: "030300") 19 | } 20 | } 21 | static var placeholderText: UIColor { 22 | if #available(iOS 13.0, *) { 23 | return .placeholderText 24 | } else { 25 | return .systemGray 26 | } 27 | } 28 | static var groupedBackground: UIColor { 29 | if #available(iOS 13.0, *) { 30 | return .secondarySystemGroupedBackground 31 | } else { 32 | return .white 33 | } 34 | } 35 | static var gray: UIColor { 36 | if #available(iOS 13.0, *) { 37 | return .systemGray3 38 | } else { 39 | return .init(hex: "d8d8dc") 40 | } 41 | } 42 | static var separator: UIColor { 43 | if #available(iOS 13.0, *) { 44 | return .separator 45 | } else { 46 | return .init(hex: "c7c7cc") 47 | } 48 | } 49 | static var blue: UIColor { 50 | return .systemBlue 51 | } 52 | static var red: UIColor { 53 | return .systemRed 54 | } 55 | 56 | // CardDisplay 57 | static var displayLabel: UIColor { 58 | return .systemGray 59 | } 60 | static var displayCvcLabel: UIColor { 61 | return .init(hex: "030300") 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Sources/Styles/Radius.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Radius.swift 3 | // PAYJP 4 | // 5 | // Created by TADASHI on 2019/12/03. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import CoreGraphics 10 | 11 | extension Style { 12 | final class Radius { 13 | static let none: CGFloat = 0 14 | static let large: CGFloat = 10 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Sources/Styles/Style.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Style.swift 3 | // PAYJP 4 | // 5 | // Created by Tadashi Wakayanagi on 2019/09/24. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | final class Style { 12 | private init() { } 13 | } 14 | -------------------------------------------------------------------------------- /Sources/ThreeDSecure/ThreeDSecureSFSafariViewControllerDriver.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ThreeDSecureSFSafariViewControllerDriver.swift 3 | // PAYJP 4 | // 5 | // Created by Tadashi Wakayanagi on 2020/04/06. 6 | // Copyright © 2020 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import SafariServices 11 | 12 | /// Web browse driver for SFSafariViewController. 13 | public class ThreeDSecureSFSafariViewControllerDriver: NSObject, ThreeDSecureWebDriver { 14 | 15 | /// Shared instance. 16 | public static let shared = ThreeDSecureSFSafariViewControllerDriver() 17 | 18 | private weak var delegate: ThreeDSecureWebDriverDelegate? 19 | 20 | public func openWebBrowser(host: UIViewController, url: URL, delegate: ThreeDSecureWebDriverDelegate) { 21 | let safariVc = SFSafariViewController(url: url) 22 | safariVc.dismissButtonStyle = .close 23 | safariVc.delegate = self 24 | safariVc.modalPresentationStyle = .overFullScreen 25 | self.delegate = delegate 26 | host.present(safariVc, animated: true, completion: nil) 27 | } 28 | 29 | public func closeWebBrowser(host: UIViewController?, completion: (() -> Void)?) -> Bool { 30 | if host is SFSafariViewController { 31 | host?.dismiss(animated: true) { 32 | completion?() 33 | } 34 | return true 35 | } 36 | return false 37 | } 38 | } 39 | 40 | // MARK: SFSafariViewControllerDelegate 41 | extension ThreeDSecureSFSafariViewControllerDriver: SFSafariViewControllerDelegate { 42 | 43 | public func safariViewControllerDidFinish(_ controller: SFSafariViewController) { 44 | delegate?.webBrowseDidFinish(self) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Sources/ThreeDSecure/ThreeDSecureURLConfiguration.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ThreeDSecureURLConfiguration.swift 3 | // PAYJP 4 | // 5 | // Created by Tadashi Wakayanagi on 2020/03/31. 6 | // 7 | 8 | import Foundation 9 | 10 | /// Configuration for using URL in 3DSecure process. 11 | @objcMembers @objc(PAYThreeDSecureURLConfiguration) 12 | public class ThreeDSecureURLConfiguration: NSObject { 13 | /// Redirect URL for launching app from web. 14 | let redirectURL: URL 15 | /// Redirect URL key. 16 | let redirectURLKey: String 17 | 18 | public init(redirectURL: URL, redirectURLKey: String) { 19 | self.redirectURL = redirectURL 20 | self.redirectURLKey = redirectURLKey 21 | } 22 | 23 | public func makeThreeDSecureEntryURL(resourceId: String) -> URL { 24 | let baseUrl = URL(string: "\(PAYJPApiEndpoint)tds/\(resourceId)")! 25 | let url = baseUrl.appendingPathComponent("start") 26 | var components = URLComponents(url: url, resolvingAgainstBaseURL: true)! 27 | components.queryItems = [ 28 | URLQueryItem(name: "publickey", value: PAYJPSDK.publicKey), 29 | URLQueryItem(name: "back", value: redirectURLKey) 30 | ] 31 | return components.url! 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Sources/ThreeDSecure/ThreeDSecureWebDriver.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ThreeDSecureWebDriver.swift 3 | // PAYJP 4 | // 5 | // Created by Tadashi Wakayanagi on 2020/04/06. 6 | // Copyright © 2020 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import SafariServices 11 | 12 | /// Delegate of web browse driver. 13 | public protocol ThreeDSecureWebDriverDelegate: AnyObject { 14 | 15 | /// Tells the delegate that web browsing is finished. 16 | /// - Parameter driver: ThreeDSecureWebDriver 17 | func webBrowseDidFinish(_ driver: ThreeDSecureWebDriver) 18 | } 19 | 20 | /// Web browse driver for 3DSecure. 21 | public protocol ThreeDSecureWebDriver { 22 | 23 | /// Open web browser. 24 | /// - Parameters: 25 | /// - host: host ViewController 26 | /// - url: load url 27 | /// - delegate: ThreeDSecureWebDriverDelegate 28 | func openWebBrowser(host: UIViewController, url: URL, delegate: ThreeDSecureWebDriverDelegate) 29 | 30 | /// Close web browser. 31 | /// - Parameters: 32 | /// - host: host ViewController 33 | /// - completion: completion action after dismiss SFSafariViewController 34 | func closeWebBrowser(host: UIViewController?, completion: (() -> Void)?) -> Bool 35 | } 36 | -------------------------------------------------------------------------------- /Sources/ThreeDSecure/ThreeDSecureWebViewControllerDriver.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import UIKit 3 | 4 | public class ThreeDSecureWebViewControllerDriver: NSObject, ThreeDSecureWebDriver { 5 | 6 | public static let shared = ThreeDSecureWebViewControllerDriver() 7 | 8 | private weak var webDriverDelegate: ThreeDSecureWebDriverDelegate? 9 | private weak var webViewController: ThreeDSecureWebViewController? 10 | 11 | public func openWebBrowser(host: UIViewController, url: URL, delegate: ThreeDSecureWebDriverDelegate) { 12 | closeWebBrowserInternal {} 13 | 14 | let webVC = ThreeDSecureWebViewController(url: url) 15 | webVC.delegate = self 16 | webVC.modalPresentationStyle = .fullScreen 17 | self.webDriverDelegate = delegate 18 | self.webViewController = webVC 19 | 20 | host.present(webVC, animated: true, completion: nil) 21 | } 22 | 23 | public func closeWebBrowser(host: UIViewController?, completion: (() -> Void)?) -> Bool { 24 | if host == nil || host === self.webViewController { 25 | return closeWebBrowserInternal(completion: completion) 26 | } 27 | completion?() 28 | return false 29 | } 30 | 31 | @discardableResult 32 | private func closeWebBrowserInternal(completion: (() -> Void)? = nil) -> Bool { 33 | guard let webVC = self.webViewController else { 34 | completion?() 35 | return false 36 | } 37 | 38 | guard webVC.presentingViewController != nil || webVC.navigationController?.isBeingDismissed == false else { 39 | completion?() 40 | return false 41 | } 42 | 43 | webVC.dismiss(animated: true) { [weak self] in 44 | self?.webViewController = nil 45 | completion?() 46 | } 47 | return true 48 | } 49 | } 50 | 51 | // MARK: - ThreeDSecureWebViewControllerDelegate 52 | extension ThreeDSecureWebViewControllerDriver: ThreeDSecureWebViewControllerDelegate { 53 | 54 | func webViewControllerDidFinish(_ controller: ThreeDSecureWebViewController, completed: Bool) { 55 | closeWebBrowserInternal { [weak self] in 56 | guard let self = self else { return } 57 | if !completed { 58 | self.webDriverDelegate?.webBrowseDidFinish(self) 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Sources/Validators/CardBrandTransformer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CardBrandTransformer.swift 3 | // PAYJP 4 | // 5 | // Created by Tadashi Wakayanagi on 2019/07/16. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol CardBrandTransformerType { 12 | /// カード番号をチェックしてカード種類に変換します 13 | /// - parameter cardNumber: カード番号 14 | /// - returns: カード種類 15 | func transform(from cardNumber: String) -> CardBrand 16 | } 17 | 18 | struct CardBrandTransformer: CardBrandTransformerType { 19 | 20 | func transform(from cardNumber: String) -> CardBrand { 21 | for brand in CardBrand.allBrands { 22 | if isMatched(source: cardNumber, regex: brand.regex) { 23 | return brand 24 | } 25 | } 26 | return .unknown 27 | } 28 | 29 | private func isMatched(source: String, regex: String) -> Bool { 30 | let predicate = NSPredicate(format: "SELF MATCHES %@", regex) 31 | let nsString = source as NSString 32 | return predicate.evaluate(with: nsString) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Sources/Validators/CardHolderValidator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CardHolderValidator.swift 3 | // PAYJP 4 | // 5 | // Created by Tatsuya Kitagawa on 2024/10/02. 6 | // Copyright © 2024 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol CardHolderValidatorType { 12 | func validate(cardHolder: String) -> CardHolderValidationResult 13 | } 14 | 15 | enum CardHolderValidationResult: Equatable { 16 | case valid 17 | case invalidCardHolderCharacter 18 | case invalidCardHolderLength 19 | } 20 | 21 | struct CardHolderValidator: CardHolderValidatorType { 22 | // swiftlint:disable force_try 23 | let regex = try! NSRegularExpression(pattern: "^[a-zA-Z0-9 \\-\\.]+$") 24 | // swiftlint:enable force_try 25 | func validate(cardHolder: String) -> CardHolderValidationResult { 26 | guard case 2...45 = cardHolder.count else { 27 | return .invalidCardHolderLength 28 | } 29 | let range = NSRange(location: 0, length: cardHolder.utf16.count) 30 | guard let _ = regex.firstMatch(in: cardHolder, options: [], range: range) else { 31 | return .invalidCardHolderCharacter 32 | } 33 | return .valid 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Sources/Validators/CardNumberValidator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CardNumberValidator.swift 3 | // PAYJP 4 | // 5 | // Created by Tadashi Wakayanagi on 2019/07/19. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol CardNumberValidatorType { 12 | /// カード番号のバリデーションチェックを行う 13 | /// 14 | /// - Parameters: 15 | /// - cardNumber: cardNumber カード番号 16 | /// - brand: brand カードブランド 17 | /// - Returns: true バリデーションOK 18 | func isValid(cardNumber: String, brand: CardBrand) -> Bool 19 | /// カード番号の長さをチェックする 20 | /// 21 | /// - Parameters: 22 | /// - cardNumber: cardNumber カード番号 23 | /// - brand: brand カードブランド 24 | /// - Returns: true 長さが正しい 25 | func isCardNumberLengthValid(cardNumber: String, brand: CardBrand) -> Bool 26 | /// カード番号のチェックディジットを行う 27 | /// 28 | /// - Parameter cardNumber: カード番号 29 | /// - Returns: true チェックディジットOK 30 | func isLuhnValid(cardNumber: String) -> Bool 31 | } 32 | 33 | struct CardNumberValidator: CardNumberValidatorType { 34 | 35 | func isValid(cardNumber: String, brand: CardBrand) -> Bool { 36 | let filtered = cardNumber.numberfy() 37 | if cardNumber.count != filtered.count { 38 | return false 39 | } 40 | return isCardNumberLengthValid(cardNumber: filtered, brand: brand) && isLuhnValid(cardNumber: filtered) 41 | } 42 | 43 | func isCardNumberLengthValid(cardNumber: String, brand: CardBrand) -> Bool { 44 | return cardNumber.count == brand.numberLength 45 | } 46 | 47 | func isLuhnValid(cardNumber: String) -> Bool { 48 | var sum = 0 49 | let digitStrings = cardNumber.reversed().map(String.init).map(Int.init) 50 | 51 | for (offset, element) in digitStrings.enumerated() { 52 | if var digit = element { 53 | let odd = offset % 2 == 1 54 | if odd { 55 | digit *= 2 56 | } 57 | if digit > 9 { 58 | digit -= 9 59 | } 60 | sum += digit 61 | } 62 | } 63 | return sum % 10 == 0 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Sources/Validators/CvcValidator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CvcValidator.swift 3 | // PAYJP 4 | // 5 | // Created by Tadashi Wakayanagi on 2019/07/19. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol CvcValidatorType { 12 | /// セキュリティコードのバリデーションチェックを行います 13 | /// 14 | /// - Parameters: 15 | /// - cvc: セキュリティコード 16 | /// - brand: カードブランド 17 | /// - Returns: バリデーションOKか、エラー即時反映か 18 | func isValid(cvc: String, brand: CardBrand) -> (validated: Bool, isInstant: Bool) 19 | } 20 | 21 | struct CvcValidator: CvcValidatorType { 22 | 23 | func isValid(cvc: String, brand: CardBrand) -> (validated: Bool, isInstant: Bool) { 24 | let filtered = cvc.numberfy() 25 | 26 | if cvc.count != filtered.count { 27 | return (false, false) 28 | } 29 | 30 | return (filtered.count == brand.cvcLength, filtered.count > brand.cvcLength) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Sources/Validators/ExpirationExtractor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExpirationExtractor.swift 3 | // PAYJP 4 | // 5 | // Created by Li-Hsuan Chen on 2019/07/18. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol ExpirationExtractorType { 12 | /// フォーマットされた文字列を月と年に分離する 13 | /// - parameter expiration: MM/yy にフォーマットされた文字列。 14 | /// - throws: 月は 1~12 の間の数字ではなかったら、エラー `monthOverflow` を投げ出します 15 | /// - returns: (month: String, year: String)? の tuple 、年は yyyy で返します。インプットは想定中なフォーマットではなかったら nil で返します。 16 | func extract(expiration: String?) throws -> (month: String, year: String)? 17 | } 18 | 19 | struct ExpirationExtractor: ExpirationExtractorType { 20 | 21 | static let shared = ExpirationExtractor() 22 | 23 | func extract(expiration: String?) throws -> (month: String, year: String)? { 24 | if let expiration = expiration, !expiration.isEmpty { 25 | let monthYear = expiration.split(separator: "/").map(String.init) 26 | 27 | switch monthYear.count { 28 | case 1: 29 | let month = monthYear[0] 30 | 31 | if month.count < 2 { return nil } 32 | 33 | guard let intMonth = Int(month) else { return nil } 34 | 35 | if !(1...12 ~= intMonth) { throw ExpirationExtractorError.monthOverflow } 36 | case 2: 37 | let month = monthYear[0] 38 | 39 | guard let intMonth = Int(month) else { return nil } 40 | 41 | if !(1...12 ~= intMonth) { throw ExpirationExtractorError.monthOverflow } 42 | 43 | let year = String(monthYear[1].unicodeScalars.prefix(2)) 44 | 45 | if year.count < 2 || Int(year) == nil { return nil } 46 | 47 | return (month, "20" + year) 48 | default: 49 | return nil 50 | } 51 | } 52 | 53 | return nil 54 | } 55 | } 56 | 57 | enum ExpirationExtractorError: Error { 58 | case monthOverflow 59 | } 60 | -------------------------------------------------------------------------------- /Sources/Validators/ExpirationValidator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExpirationValidator.swift 3 | // PAYJP 4 | // 5 | // Created by Li-Hsuan Chen on 2019/07/19. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol ExpirationValidatorType { 12 | /// 月と年は今より未来なのかをチェックする 13 | /// - parameter month: 月 14 | /// - parameter year: 年 15 | /// - returns: 渡された年月は `>= 今月` であれば true 、 `< 今月` であれば false 16 | func isValid(month: String, year: String) -> Bool 17 | } 18 | 19 | struct ExpirationValidator: ExpirationValidatorType { 20 | 21 | static let shared = ExpirationValidator() 22 | 23 | func isValid(month: String, year: String) -> Bool { 24 | guard let intMonth = Int(month), let intYear = Int(year) else { return false } 25 | 26 | let moddedIntYear = intYear % 100 27 | 28 | let currentMonth = self.currentMonth() 29 | let currentYear = self.currentYear() 30 | 31 | if moddedIntYear == currentYear { 32 | return intMonth >= currentMonth 33 | } else { 34 | return moddedIntYear > currentYear 35 | } 36 | } 37 | 38 | private func currentYear() -> Int { 39 | let calendar = Calendar(identifier: .gregorian) 40 | let year = calendar.component(.year, from: Date()) 41 | return year % 100 42 | } 43 | 44 | private func currentMonth() -> Int { 45 | let calendar = Calendar(identifier: .gregorian) 46 | let month = calendar.component(.month, from: Date()) 47 | return month 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Sources/Validators/PublicKeyValidator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PublicKeyValidator.swift 3 | // PAYJP 4 | // 5 | // Created by Tadashi Wakayanagi on 2019/11/27. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol PublicKeyValidatorType { 12 | func validate(publicKey key: String) 13 | } 14 | 15 | struct PublicKeyValidator: PublicKeyValidatorType { 16 | 17 | static let shared = PublicKeyValidator() 18 | 19 | private let localAssert: (Bool, String, StaticString, UInt) -> Void 20 | 21 | /// テストのためにassert関数を置き換えられるようにしている 22 | /// デフォルトで Swift.assert を使用 23 | /// see PublicKeyValidatorTests 24 | init(assert: @escaping (Bool, String, StaticString, UInt) -> Void = {Swift.assert($0, $1, file: $2, line: $3)}) { 25 | localAssert = assert 26 | } 27 | 28 | func validate(publicKey key: String) { 29 | let trimmed = key.trimmingCharacters(in: .whitespaces) 30 | localAssert(!trimmed.isEmpty, 31 | "❌You need to set publickey for PAY.JP. You can find in https://pay.jp/d/settings .", #file, #line) 32 | localAssert(!key.hasPrefix("sk_"), "❌You are using secretkey (`sk_xxxx`) instead of PAY.JP publickey." + 33 | "You can find **public** key like `pk_xxxxxx` in https://pay.jp/d/settings .", #file, #line) 34 | 35 | if key.hasPrefix("pk_test") { 36 | print(debug: "⚠️PAY.JP now use **TEST** mode key." + 37 | "In production, you should use livemode key like `pk_live_xxxx`.") 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Sources/Views/ActionButton.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ActionButton.swift 3 | // PAYJP 4 | // 5 | // Created by TADASHI on 2019/12/03. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ActionButton: UIButton { 12 | 13 | @IBInspectable var normalBackgroundColor: UIColor = Style.Color.blue { 14 | didSet { 15 | if isEnabled { 16 | self.backgroundColor = normalBackgroundColor 17 | } 18 | } 19 | } 20 | @IBInspectable var disableBackgroundColor: UIColor = Style.Color.gray { 21 | didSet { 22 | if !isEnabled { 23 | self.backgroundColor = disableBackgroundColor 24 | } 25 | } 26 | } 27 | @IBInspectable var cornerRadius: CGFloat = Style.Radius.large { 28 | didSet { 29 | self.layer.cornerRadius = cornerRadius 30 | } 31 | } 32 | 33 | // MARK: - Super Class 34 | 35 | override init(frame: CGRect) { 36 | super.init(frame: frame) 37 | initialize() 38 | } 39 | 40 | required init?(coder decoder: NSCoder) { 41 | super.init(coder: decoder) 42 | initialize() 43 | } 44 | 45 | override var isEnabled: Bool { 46 | didSet { 47 | if isEnabled { 48 | self.backgroundColor = self.normalBackgroundColor 49 | } else { 50 | self.backgroundColor = self.disableBackgroundColor 51 | } 52 | } 53 | } 54 | 55 | // MARK: - Helper Methods 56 | 57 | private func initialize() { 58 | self.layer.cornerRadius = self.cornerRadius 59 | self.setTitleColor(.white, for: .normal) 60 | self.setTitleColor(.white, for: .disabled) 61 | 62 | self.titleLabel?.font = UIFont.systemFont(ofSize: 14, weight: UIFont.Weight.bold) 63 | self.backgroundColor = (self.isEnabled) ? self.normalBackgroundColor : self.disableBackgroundColor 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Sources/Views/BorderView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BorderView.swift 3 | // PAYJP 4 | // 5 | // Created by Tadashi Wakayanagi on 2020/04/13. 6 | // Copyright © 2020 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @IBDesignable 12 | class BorderView: UIView { 13 | 14 | @IBInspectable var cornerRadius: CGFloat = 0 { 15 | didSet { 16 | layer.cornerRadius = cornerRadius 17 | layer.masksToBounds = cornerRadius > 0 18 | } 19 | } 20 | @IBInspectable var borderWidth: CGFloat = 0 { 21 | didSet { 22 | layer.borderWidth = borderWidth 23 | } 24 | } 25 | @IBInspectable var borderColor: UIColor? { 26 | didSet { 27 | layer.borderColor = borderColor?.cgColor 28 | } 29 | } 30 | @IBInspectable var isHighlighted: Bool = false { 31 | didSet { 32 | if oldValue != isHighlighted { 33 | updateHighlight() 34 | } 35 | } 36 | } 37 | 38 | override func layoutSubviews() { 39 | super.layoutSubviews() 40 | updateHighlight() 41 | } 42 | 43 | private func updateHighlight() { 44 | if isHighlighted { 45 | highlightOn() 46 | } else { 47 | highlightOff() 48 | } 49 | } 50 | 51 | private func highlightOn() { 52 | layer.cornerRadius = cornerRadius 53 | layer.masksToBounds = cornerRadius > 0 54 | layer.borderWidth = borderWidth 55 | layer.borderColor = borderColor?.cgColor 56 | } 57 | 58 | private func highlightOff() { 59 | layer.cornerRadius = 0 60 | layer.masksToBounds = false 61 | layer.borderWidth = 0 62 | layer.borderColor = nil 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Sources/Views/BrandImageCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BrandImageCell.swift 3 | // PAYJP 4 | // 5 | // Created by Tadashi Wakayanagi on 2019/11/22. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class BrandImageCell: UICollectionViewCell { 12 | 13 | @IBOutlet weak var brandImage: UIImageView! 14 | 15 | func setup(brand: CardBrand) { 16 | brandImage.image = brand.logoImage 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Sources/Views/CardFormInput.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CardFormInput.swift 3 | // PAYJP 4 | // 5 | // Created by Tadashi Wakayanagi on 2019/12/05. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct CardFormInput { 12 | let cardNumber: String 13 | let expirationMonth: String 14 | let expirationYear: String 15 | let cvc: String 16 | let cardHolder: String? 17 | let email: String? 18 | let phoneNumber: String? 19 | } 20 | -------------------------------------------------------------------------------- /Sources/Views/CardFormViewControllerDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CardFormViewControllerDelegate.swift 3 | // PAYJP 4 | // 5 | // Created by Tadashi Wakayanagi on 2019/11/19. 6 | // 7 | 8 | import Foundation 9 | 10 | /// CardFormViewController delegate. 11 | @objc(PAYCardFormViewControllerDelegate) 12 | public protocol CardFormViewControllerDelegate: AnyObject { 13 | 14 | /// Callback when card form operation is completed. 15 | /// 16 | /// - Parameter result: CardFormResult 17 | func cardFormViewController(_: CardFormViewController, didCompleteWith result: CardFormResult) 18 | 19 | /// Callback when creating token is completed. 20 | /// 21 | /// - Parameters: 22 | /// - token: token created by card form 23 | /// - completionHandler: completion action 24 | func cardFormViewController(_: CardFormViewController, 25 | didProduced token: Token, 26 | completionHandler: @escaping (Error?) -> Void) 27 | } 28 | 29 | /// Result of card form operation. 30 | @objc public enum CardFormResult: Int { 31 | /// when saving token is successful 32 | case success = 0 33 | /// when card form screen is closed 34 | case cancel = 1 35 | } 36 | -------------------------------------------------------------------------------- /Sources/Views/CardNumber.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CardNumber.swift 3 | // PAYJP 4 | // 5 | // Created by Tadashi Wakayanagi on 2019/07/30. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct CardNumber { 12 | let value: String 13 | let formatted: String 14 | let brand: CardBrand 15 | let display: String 16 | let mask: String 17 | } 18 | -------------------------------------------------------------------------------- /Sources/Views/ErrorTranslator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ErrorTranslator.swift 3 | // PAYJP 4 | // 5 | // Created by Tadashi Wakayanagi on 2019/11/29. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol ErrorTranslatorType { 12 | func translate(error: Error) -> String 13 | } 14 | 15 | struct ErrorTranslator: ErrorTranslatorType { 16 | 17 | static let shared = ErrorTranslator() 18 | 19 | func translate(error: Error) -> String { 20 | if let apiError = error as? APIError { 21 | switch apiError { 22 | case .serviceError(let response): 23 | let codeDescription = " (code:\(response.code ?? "none"))" 24 | switch response.status { 25 | case 402: 26 | return response.message ?? "payjp_card_form_screen_error_unknown".localized + codeDescription 27 | case 500..<600: 28 | return "payjp_card_form_screen_error_server".localized + codeDescription 29 | default: 30 | return "payjp_card_form_screen_error_application".localized + codeDescription 31 | } 32 | case .rateLimitExceeded: 33 | return "payjp_card_form_screen_error_rate_limit_exceeded".localized 34 | case .systemError(let error): 35 | return error.localizedDescription 36 | default: 37 | return "payjp_card_form_screen_error_unknown".localized 38 | } 39 | } 40 | return error.localizedDescription 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Sources/Views/ErrorView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ErrorView.swift 3 | // PAYJP 4 | // 5 | // Created by Tadashi Wakayanagi on 2019/11/28. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol ErrorViewDelegate: AnyObject { 12 | func reload() 13 | } 14 | 15 | class ErrorView: UIView { 16 | 17 | @IBOutlet weak var errorMessageLabel: UILabel! 18 | @IBOutlet weak var reloadButton: UIButton! 19 | 20 | @IBAction func reloadTapped(_ sender: Any) { 21 | delegate?.reload() 22 | } 23 | 24 | private var contentView: UIView! 25 | weak var delegate: ErrorViewDelegate? 26 | 27 | override public init(frame: CGRect) { 28 | super.init(frame: frame) 29 | initialize() 30 | } 31 | 32 | required public init?(coder aDecoder: NSCoder) { 33 | super.init(coder: aDecoder) 34 | initialize() 35 | } 36 | 37 | private func initialize() { 38 | let nib = UINib(nibName: "ErrorView", bundle: .payjpBundle) 39 | let view = nib.instantiate(withOwner: self, options: nil).first as? UIView 40 | 41 | if let view = view { 42 | contentView = view 43 | view.frame = bounds 44 | view.autoresizingMask = [.flexibleWidth, .flexibleHeight] 45 | addSubview(view) 46 | } 47 | } 48 | 49 | override public var intrinsicContentSize: CGSize { 50 | return contentView.intrinsicContentSize 51 | } 52 | 53 | func show(message: String, reloadButtonHidden: Bool) { 54 | self.isHidden = false 55 | errorMessageLabel.text = message 56 | reloadButton.isHidden = reloadButtonHidden 57 | } 58 | 59 | func dismiss() { 60 | self.isHidden = true 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Sources/Views/Expiration.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Expiration.swift 3 | // PAYJP 4 | // 5 | // Created by Tadashi Wakayanagi on 2020/04/24. 6 | // Copyright © 2020 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct Expiration { 12 | let formatted: String 13 | let display: String 14 | } 15 | -------------------------------------------------------------------------------- /Sources/Views/ExtraAttribute.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExtraAttribute.swift 3 | // PAYJP 4 | // 5 | // Created by Tatsuya Kitagawa on 2024/08/20. 6 | // Copyright © 2024 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// Extra attributes for card form. 12 | /// For now it is mainly used for 3-D Secure. 13 | /// see [https://help.pay.jp/ja/articles/9556161] 14 | @objc(PAYExtraAttribute) 15 | public protocol ExtraAttribute {} 16 | 17 | @objc(PAYExtraAttributeEmail) 18 | public final class ExtraAttributeEmail: NSObject, ExtraAttribute { 19 | public let preset: String? 20 | 21 | /// - parameters: 22 | /// - preset: preset email for card form. 23 | @objc 24 | public init(preset: String? = nil) { 25 | self.preset = preset 26 | } 27 | } 28 | 29 | @objc(PAYExtraAttributePhone) 30 | public final class ExtraAttributePhone: NSObject, ExtraAttribute { 31 | public let presetNumber: String? 32 | public let presetRegion: String? 33 | 34 | /// - parameters: 35 | /// - presetNumber: preset phone number for card form. You can pass either a local phone number or an international phone number format. 36 | /// - presetRegion: preset region code for phone number. (ISO 3166-1 alpha-2) e.g.`JP` 37 | @objc 38 | public init(presetNumber: String? = nil, presetRegion: String? = nil) { 39 | self.presetNumber = presetNumber 40 | self.presetRegion = presetRegion 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Sources/Views/FormError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FormError.swift 3 | // PAYJP 4 | // 5 | // Created by Tadashi Wakayanagi on 2019/07/30. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | enum FormError: Error { 12 | case cardNumberEmptyError(value: CardNumber?, isInstant: Bool) 13 | case cardNumberInvalidError(value: CardNumber?, isInstant: Bool) 14 | case cardNumberInvalidBrandError(value: CardNumber?, isInstant: Bool) 15 | case expirationEmptyError(value: Expiration?, isInstant: Bool) 16 | case expirationInvalidError(value: Expiration?, isInstant: Bool) 17 | case cvcEmptyError(value: String?, isInstant: Bool) 18 | case cvcInvalidError(value: String?, isInstant: Bool) 19 | case cardHolderEmptyError(value: String?, isInstant: Bool) 20 | case cardHolderInvalidError(value: String?, isInstant: Bool) 21 | case cardHolderInvalidLengthError(value: String?, isInstant: Bool) 22 | case emailEmptyError(value: String?, isInstant: Bool) 23 | case phoneNumberEmptyError(value: String?, isInstant: Bool) 24 | case phoneNumberInvalidError(value: String?, isInstant: Bool) 25 | } 26 | 27 | extension FormError: LocalizedError { 28 | var errorDescription: String? { 29 | switch self { 30 | case .cardNumberEmptyError: 31 | return "payjp_card_form_error_no_number".localized 32 | case .cardNumberInvalidError: 33 | return "payjp_card_form_error_invalid_number".localized 34 | case .cardNumberInvalidBrandError: 35 | return "payjp_card_form_error_invalid_brand".localized 36 | case .expirationEmptyError: 37 | return "payjp_card_form_error_no_expiration".localized 38 | case .expirationInvalidError: 39 | return "payjp_card_form_error_invalid_expiration".localized 40 | case .cvcEmptyError: 41 | return "payjp_card_form_error_no_cvc".localized 42 | case .cvcInvalidError: 43 | return "payjp_card_form_error_invalid_cvc".localized 44 | case .cardHolderEmptyError: 45 | return "payjp_card_form_error_no_holder_name".localized 46 | case .cardHolderInvalidError: 47 | return "payjp_card_form_error_invalid_holder_name".localized 48 | case .cardHolderInvalidLengthError: 49 | return "payjp_card_form_error_invalid_holder_name_length".localized 50 | case .emailEmptyError: 51 | return "payjp_card_form_error_no_email".localized 52 | case .phoneNumberEmptyError: 53 | return "payjp_card_form_error_no_phone_number".localized 54 | case .phoneNumberInvalidError: 55 | return "payjp_card_form_error_invalid_phone_number".localized 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Sources/Views/FormStyle.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FormStyle.swift 3 | // PAYJP 4 | // 5 | // Created by TADASHI on 2019/09/20. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /// CardForm style settings. 12 | /// It's possible to change the color of each UIComponent. 13 | @objcMembers @objc(PAYCardFormStyle) 14 | public class FormStyle: NSObject { 15 | 16 | /// Default style. 17 | public static let defaultStyle: FormStyle = { 18 | let style = FormStyle(labelTextColor: Style.Color.label, 19 | inputTextColor: Style.Color.label, 20 | errorTextColor: Style.Color.red, 21 | tintColor: Style.Color.blue, 22 | inputFieldBackgroundColor: Style.Color.groupedBackground, 23 | submitButtonColor: Style.Color.blue, 24 | highlightColor: Style.Color.blue) 25 | return style 26 | }() 27 | 28 | /// Text color of UILabel. 29 | public let labelTextColor: UIColor 30 | /// Text color of UITextField. 31 | public let inputTextColor: UIColor 32 | /// Text color of Error. 33 | public let errorTextColor: UIColor 34 | /// Tint color of UITextField. 35 | public let tintColor: UIColor 36 | /// Background color of UITextField. 37 | public let inputFieldBackgroundColor: UIColor 38 | /// Background color of UIButton. 39 | public let submitButtonColor: UIColor 40 | /// Highlight color of Card display label. 41 | public let highlightColor: UIColor 42 | 43 | public init(labelTextColor: UIColor? = nil, 44 | inputTextColor: UIColor? = nil, 45 | errorTextColor: UIColor? = nil, 46 | tintColor: UIColor? = nil, 47 | inputFieldBackgroundColor: UIColor? = nil, 48 | submitButtonColor: UIColor? = nil, 49 | highlightColor: UIColor? = nil) { 50 | self.labelTextColor = labelTextColor ?? Style.Color.label 51 | self.inputTextColor = inputTextColor ?? Style.Color.label 52 | self.errorTextColor = errorTextColor ?? Style.Color.red 53 | self.tintColor = tintColor ?? Style.Color.blue 54 | self.inputFieldBackgroundColor = inputFieldBackgroundColor ?? Style.Color.groupedBackground 55 | self.submitButtonColor = submitButtonColor ?? Style.Color.blue 56 | self.highlightColor = highlightColor ?? Style.Color.blue 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Sources/Views/FormTextField.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FormTextField.swift 3 | // PAYJP 4 | // 5 | // Created by Tadashi Wakayanagi on 2020/04/30. 6 | // Copyright © 2020 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol FormTextFieldDelegate: AnyObject { 12 | func didDeleteBackward(_ textField: FormTextField) 13 | } 14 | 15 | /// UITextFieldでdeleteキーが押されたことを検知する 16 | class FormTextField: UITextField { 17 | weak var deletionDelegate: FormTextFieldDelegate? 18 | 19 | override func deleteBackward() { 20 | super.deleteBackward() 21 | deletionDelegate?.didDeleteBackward(self) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Sources/Views/PresetPhoneNumberTextField.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PresetPhoneNumberTextField.swift 3 | // PAYJP 4 | // 5 | // Created by Tatsuya Kitagawa on 2024/08/22. 6 | // Copyright © 2024 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import PhoneNumberKit 11 | 12 | class PresetPhoneNumberTextField: PhoneNumberTextField { 13 | override var defaultRegion: String { 14 | get { 15 | presetRegion ?? super.defaultRegion 16 | } 17 | set { 18 | // no-op (for only compatibility) 19 | } 20 | } 21 | 22 | var presetRegion: String? { 23 | didSet { 24 | if let presetRegion { 25 | partialFormatter.defaultRegion = presetRegion 26 | updatePlaceholder() 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Tests/CardFromTokenTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CardFromTokenTests.swift 3 | // PAYJP 4 | // 5 | // Created by k@binc.jp on 2017/01/05. 6 | // Copyright © 2017 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import XCTest 11 | @testable import PAYJP 12 | 13 | // swiftlint:disable force_try 14 | class CardFromTokenTests: XCTestCase { 15 | var card: Card! 16 | 17 | override func setUp() { 18 | let json = TestFixture.JSON(by: "token.json") 19 | let decoder = JSONDecoder.shared 20 | let token = try! Token.decodeJson(with: json, using: decoder) 21 | card = token.card 22 | } 23 | 24 | func testCardProperties() { 25 | XCTAssertEqual(card.brand, "Visa") 26 | XCTAssertEqual(card.identifer, "car_202e275bfde8d17eb2e0444ff767") 27 | XCTAssertEqual(card.last4Number, "0300") 28 | XCTAssertEqual(card.brand, "Visa") 29 | XCTAssertEqual(card.expirationMonth, 12) 30 | XCTAssertEqual(card.expirationYear, 2018) 31 | XCTAssertEqual(card.fingerprint, "35c45684bce0412a22a515f432d40be8") 32 | XCTAssertEqual(card.name, "TARO YAMADA") 33 | XCTAssertEqual(card.threeDSecureStatus, PAYThreeDSecureStatus.verified) 34 | XCTAssertEqual(card.email, "test@example.com") 35 | XCTAssertEqual(card.phone, "+819012345678") 36 | } 37 | 38 | // swiftlint:disable force_cast 39 | func testCardMetadata() { 40 | let rawValue = card.rawValue! 41 | let metadata = rawValue["metadata"] as! [String: Any] 42 | XCTAssertEqual(metadata.count, 1) 43 | XCTAssertEqual(metadata["foo"] as! String, "bar") 44 | } 45 | // swiftlint:enable force_cast 46 | } 47 | // swiftlint:enable force_try 48 | -------------------------------------------------------------------------------- /Tests/Core/PAYJPSDKTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // PAYJPSDKTests.m 3 | // PAYJPTests 4 | // 5 | // Created by Li-Hsuan Chen on 2019/07/24. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | @import PAYJP; 11 | 12 | @interface PAYJPSDKTests : XCTestCase 13 | @end 14 | 15 | @implementation PAYJPSDKTests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | 20 | PAYJPSDK.publicKey = nil; 21 | PAYJPSDK.locale = nil; 22 | } 23 | 24 | - (void)testValuesSet { 25 | NSString *mockPublicKey = @"publicKey"; 26 | NSLocale *mockLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"ja"]; 27 | 28 | PAYJPSDK.publicKey = mockPublicKey; 29 | PAYJPSDK.locale = mockLocale; 30 | 31 | XCTAssertEqual(PAYJPSDK.publicKey, mockPublicKey); 32 | XCTAssertEqual(PAYJPSDK.locale.localeIdentifier, mockLocale.localeIdentifier); 33 | } 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /Tests/Core/PAYJPSDKTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PAYJPSDKTests.swift 3 | // PAYJPTests 4 | // 5 | // Created by Li-Hsuan Chen on 2019/07/24. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import PAYJP 11 | 12 | class PAYJPSDKTests: XCTestCase { 13 | override func setUp() { 14 | super.setUp() 15 | 16 | PAYJPSDK.publicKey = nil 17 | PAYJPSDK.locale = nil 18 | } 19 | 20 | func testValueSet() { 21 | let mockPublicKey = "publicKey" 22 | let mockLocale = Locale(identifier: "ja") 23 | 24 | PAYJPSDK.publicKey = mockPublicKey 25 | PAYJPSDK.locale = mockLocale 26 | 27 | XCTAssertEqual(PAYJPSDK.publicKey, mockPublicKey) 28 | XCTAssertEqual(PAYJPSDK.locale, mockLocale) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Tests/Fixtures/cardBrands.json: -------------------------------------------------------------------------------- 1 | { 2 | "card_types_supported": [ 3 | "Visa", 4 | "MasterCard", 5 | "JCB", 6 | "American Express", 7 | "Diners Club", 8 | "Discover" 9 | ], 10 | "livemode": false 11 | } 12 | -------------------------------------------------------------------------------- /Tests/Fixtures/error.json: -------------------------------------------------------------------------------- 1 | { 2 | "error": { 3 | "status": 402, 4 | "message": "Invalid card number", 5 | "param": "card[number]", 6 | "code": "invalid_number", 7 | "type": "card_error" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Tests/Fixtures/invalid_apple_pay_token.json: -------------------------------------------------------------------------------- 1 | { 2 | "error": { 3 | "code": "invalid_apple_pay_token", 4 | "message": "Invalid Apple Pay token.", 5 | "param": "card", 6 | "status": 400, 7 | "type": "client_error" 8 | } 9 | } -------------------------------------------------------------------------------- /Tests/Fixtures/token.json: -------------------------------------------------------------------------------- 1 | { 2 | "card": { 3 | "address_city": null, 4 | "address_line1": null, 5 | "address_line2": null, 6 | "address_state": null, 7 | "address_zip": null, 8 | "address_zip_check": "unchecked", 9 | "brand": "Visa", 10 | "country": null, 11 | "created": 1475462082, 12 | "customer": null, 13 | "cvc_check": "unchecked", 14 | "exp_month": 12, 15 | "exp_year": 2018, 16 | "fingerprint": "35c45684bce0412a22a515f432d40be8", 17 | "id": "car_202e275bfde8d17eb2e0444ff767", 18 | "last4": "0300", 19 | "livemode": true, 20 | "metadata": { 21 | "foo": "bar" 22 | }, 23 | "name": "TARO YAMADA", 24 | "object": "card", 25 | "three_d_secure_status": "verified", 26 | "email": "test@example.com", 27 | "phone": "+819012345678" 28 | }, 29 | "created": 1475462082, 30 | "id": "tok_bba03649fecef2d367be6fc28367", 31 | "livemode": true, 32 | "object": "token", 33 | "used": false 34 | } -------------------------------------------------------------------------------- /Tests/Formatters/CvcFormatterTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CvcFormatterTests.swift 3 | // PAYJPTests 4 | // 5 | // Created by Tadashi Wakayanagi on 2019/07/19. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import PAYJP 11 | 12 | class CvcFormatterTests: XCTestCase { 13 | 14 | let formatter = CvcFormatter() 15 | 16 | private func testFortmat(cases: [(String?, CardBrand, String?)]) { 17 | for (cvc, brand, formatted) in cases { 18 | let output = formatter.string(from: cvc, brand: brand) 19 | XCTAssertEqual(output, formatted) 20 | } 21 | } 22 | 23 | func testCvcFormat() { 24 | let cases: [(String?, CardBrand, String?)] = [ 25 | (nil, .unknown, nil), 26 | (nil, .visa, nil), 27 | (nil, .americanExpress, nil), 28 | ("", .unknown, nil), 29 | ("", .mastercard, nil), 30 | ("", .americanExpress, nil), 31 | ("1", .unknown, "1"), 32 | ("1", .jcb, "1"), 33 | ("1", .americanExpress, "1"), 34 | ("12", .unknown, "12"), 35 | ("12", .dinersClub, "12"), 36 | ("12", .americanExpress, "12"), 37 | ("123", .unknown, "123"), 38 | ("123", .discover, "123"), 39 | ("123", .americanExpress, "123"), 40 | ("1234", .unknown, "1234"), 41 | ("1234", .visa, "123"), 42 | ("1234", .americanExpress, "1234"), 43 | ("12345", .unknown, "1234"), 44 | ("12345", .mastercard, "123"), 45 | ("12345", .americanExpress, "1234"), 46 | ("aaa", .unknown, nil), 47 | ("aaa", .jcb, nil), 48 | ("aaa", .americanExpress, nil) 49 | ] 50 | testFortmat(cases: cases) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Tests/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 | BNDL 17 | CFBundleShortVersionString 18 | 2.2.1 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Tests/Networking/Core/ClientInfoTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ClientInfoTests.swift 3 | // PAYJPTests 4 | // 5 | // Created by Tatsuya Kitagawa on 2020/02/06. 6 | // Copyright © 2020 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import OHHTTPStubs 11 | @testable import PAYJP 12 | 13 | class ClientInfoTests: XCTestCase { 14 | 15 | func testCreate() { 16 | let clientInfo = ClientInfo.makeInfo() 17 | XCTAssertEqual(clientInfo.bindingsName, "jp.pay.ios") 18 | XCTAssertEqual(clientInfo.platform, "ios") 19 | XCTAssertEqual(clientInfo.bindingsPlugin, nil) 20 | XCTAssertEqual(clientInfo.publisher, "payjp") 21 | } 22 | 23 | func testCreate_plugin() { 24 | let clientInfo = ClientInfo.makeInfo(plugin: "jp.pay.kitagawa/1.0.0", publisher: "kitagawa") 25 | XCTAssertEqual(clientInfo.bindingsName, "jp.pay.ios") 26 | XCTAssertEqual(clientInfo.platform, "ios") 27 | XCTAssertEqual(clientInfo.bindingsPlugin, "jp.pay.kitagawa/1.0.0") 28 | XCTAssertEqual(clientInfo.publisher, "kitagawa") 29 | } 30 | 31 | func testBindingVersion() { 32 | let clientInfo = ClientInfo( 33 | bindingsName: "jp.pay.ios", 34 | bindingsVersion: "1.0.0", 35 | bindingsPlugin: nil, 36 | unameString: "iPhone X", 37 | platform: "ios", 38 | publisher: "payjp") 39 | XCTAssertEqual(clientInfo.bindingInfo, "jp.pay.ios/1.0.0") 40 | } 41 | 42 | func testBindingVersion_withPlugin() { 43 | let clientInfo = ClientInfo( 44 | bindingsName: "jp.pay.ios", 45 | bindingsVersion: "1.0.0", 46 | bindingsPlugin: "jp.pay.kitagawa/1.0.0", 47 | unameString: "iPhone X", 48 | platform: "ios", 49 | publisher: "payjp") 50 | XCTAssertEqual(clientInfo.bindingInfo, "jp.pay.ios/1.0.0@jp.pay.kitagawa/1.0.0") 51 | } 52 | 53 | func testUserAgent() { 54 | let clientInfo = ClientInfo( 55 | bindingsName: "jp.pay.ios", 56 | bindingsVersion: "1.0.0", 57 | bindingsPlugin: nil, 58 | unameString: "iPhone X", 59 | platform: "ios", 60 | publisher: "payjp") 61 | XCTAssertEqual(clientInfo.userAgent, "jp.pay.ios/1.0.0; iPhone X") 62 | } 63 | 64 | func testJson() { 65 | let clientInfo = ClientInfo( 66 | bindingsName: "jp.pay.ios", 67 | bindingsVersion: "1.1.0", 68 | bindingsPlugin: "jp.pay.kitagawa/1.0.0", 69 | unameString: "iPhoneX", 70 | platform: "ios", 71 | publisher: "payjp") 72 | XCTAssertNotNil(clientInfo.json) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Tests/Networking/Models/GetAcceptedBrandsResponseTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GetAcceptedBrandsResponseTests.swift 3 | // PAYJPTests 4 | // 5 | // Created by Li-Hsuan Chen on 2019/07/31. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import PAYJP 11 | 12 | class GetAcceptedBrandsResponseTests: XCTestCase { 13 | func testDecoding() { 14 | let jsonData = """ 15 | {"card_types_supported": ["Visa"], "livemode": true} 16 | """.data(using: .utf8)! 17 | 18 | let response = try? JSONDecoder.shared.decode(GetAcceptedBrandsResponse.self, from: jsonData) 19 | 20 | XCTAssertEqual(response?.acceptedBrands.count, 1) 21 | XCTAssertEqual(response?.acceptedBrands.first, .visa) 22 | XCTAssertEqual(response?.liveMode, true) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Tests/Networking/Models/PAYErrorResponseTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PAYErrorResponseTests.swift 3 | // PAYJP 4 | // 5 | // Created by Li-Hsuan Chen on 2018/05/21. 6 | // Copyright © 2018年 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | import XCTest 12 | @testable import PAYJP 13 | 14 | class PAYErrorResponseTests: XCTestCase { 15 | var errorResponse: PAYErrorResponse! 16 | 17 | override func setUp() { 18 | let json = TestFixture.JSON(by: "error.json") 19 | let decoder = JSONDecoder.shared 20 | // swiftlint:disable force_try 21 | errorResponse = try! decoder.decode(PAYErrorResult.self, from: json).error 22 | // swiftlint:enable force_try 23 | } 24 | 25 | func testErrorProperties() { 26 | XCTAssertEqual(errorResponse.status, 402) 27 | XCTAssertEqual(errorResponse.message, "Invalid card number") 28 | XCTAssertEqual(errorResponse.param, "card[number]") 29 | XCTAssertEqual(errorResponse.code, "invalid_number") 30 | XCTAssertEqual(errorResponse.type, "card_error") 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Tests/Networking/Models/ThreeDSecureStatusTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ThreeDSecureStatusTests.swift 3 | // PAYJPTests 4 | // 5 | // Created by Tadashi Wakayanagi on 2020/04/02. 6 | // Copyright © 2020 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import PAYJP 11 | 12 | class ThreeDSecureStatusTests: XCTestCase { 13 | 14 | func testFind_verified() { 15 | let rawValue = PAYThreeDSecureStatus.verified.rawValue 16 | let status = ThreeDSecureStatus.find(rawValue: rawValue) 17 | XCTAssertEqual(status, PAYThreeDSecureStatus.verified) 18 | } 19 | 20 | func testFind_attempted() { 21 | let rawValue = PAYThreeDSecureStatus.attempted.rawValue 22 | let status = ThreeDSecureStatus.find(rawValue: rawValue) 23 | XCTAssertEqual(status, PAYThreeDSecureStatus.attempted) 24 | } 25 | 26 | func testFind_unverified() { 27 | let rawValue = PAYThreeDSecureStatus.unverified.rawValue 28 | let status = ThreeDSecureStatus.find(rawValue: rawValue) 29 | XCTAssertEqual(status, PAYThreeDSecureStatus.unverified) 30 | } 31 | 32 | func testFind_failed() { 33 | let rawValue = PAYThreeDSecureStatus.failed.rawValue 34 | let status = ThreeDSecureStatus.find(rawValue: rawValue) 35 | XCTAssertEqual(status, PAYThreeDSecureStatus.failed) 36 | } 37 | 38 | func testFind_aborted() { 39 | let rawValue = PAYThreeDSecureStatus.aborted.rawValue 40 | let status = ThreeDSecureStatus.find(rawValue: rawValue) 41 | XCTAssertEqual(status, PAYThreeDSecureStatus.aborted) 42 | } 43 | 44 | func testFind_error() { 45 | let rawValue = PAYThreeDSecureStatus.error.rawValue 46 | let status = ThreeDSecureStatus.find(rawValue: rawValue) 47 | XCTAssertEqual(status, PAYThreeDSecureStatus.error) 48 | } 49 | 50 | func testFind_unknown() { 51 | let rawValue = "unknown" 52 | let status = ThreeDSecureStatus.find(rawValue: rawValue) 53 | XCTAssertNil(status) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Tests/Networking/Models/TokenTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TokenTests.swift 3 | // PAYJP 4 | // 5 | // Created by k@binc.jp on 10/3/16. 6 | // Copyright © 2016 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import XCTest 11 | @testable import PAYJP 12 | 13 | class TokenTests: XCTestCase { 14 | var token: Token! 15 | var json: Data! 16 | 17 | override func setUp() { 18 | json = TestFixture.JSON(by: "token.json") 19 | let decoder = JSONDecoder.shared 20 | // swiftlint:disable force_try 21 | token = try! Token.decodeJson(with: json, using: decoder) 22 | // swiftlint:enable force_try 23 | } 24 | 25 | func testTokenProperties() { 26 | XCTAssertEqual(token.identifer, "tok_bba03649fecef2d367be6fc28367") 27 | XCTAssertEqual(token.livemode, true) 28 | XCTAssertEqual(token.used, false) 29 | } 30 | 31 | func testCreatedDate() { 32 | XCTAssertEqual(token.createdAt, Date(timeIntervalSince1970: 1475462082)) 33 | } 34 | 35 | func testRawObject() { 36 | let rawValue = token.rawValue 37 | XCTAssertEqual(rawValue?.count, 6) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Tests/Networking/NSErrorConverterTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSErrorConverter.swift 3 | // PAYJPTests 4 | // 5 | // Created by Tadashi Wakayanagi on 2019/10/07. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import PAYJP 11 | 12 | class NSErrorConverterTests: XCTestCase { 13 | 14 | let converter = NSErrorConverter() 15 | 16 | func testConvertAPIError() { 17 | let error = APIError.invalidResponse(nil) 18 | let nsError = converter.convert(from: error) 19 | XCTAssertEqual(nsError?.domain, PAYErrorDomain) 20 | XCTAssertEqual(nsError?.code, PAYErrorInvalidResponse) 21 | let description = nsError?.userInfo[NSLocalizedDescriptionKey] as? String 22 | XCTAssertEqual(description, "The response is not a HTTPURLResponse instance.") 23 | } 24 | 25 | func testConvertOtherError() { 26 | let error = NSError(domain: "other", code: 100, userInfo: nil) 27 | let nsError = converter.convert(from: error) 28 | XCTAssertEqual(nsError?.domain, PAYErrorDomain) 29 | XCTAssertEqual(nsError?.code, PAYErrorSystemError) 30 | XCTAssertNotNil(nsError?.userInfo) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Tests/Networking/Requests/CreateTokenForApplePayRequestTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CreateTokenForApplePayRequestTests.swift 3 | // PAYJPTests 4 | // 5 | // Created by Li-Hsuan Chen on 2019/07/25. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import PAYJP 11 | 12 | class CreateTokenForApplePayRequestTests: XCTestCase { 13 | func testInitialization() { 14 | let request = CreateTokenForApplePayRequest(paymentToken: "token") 15 | 16 | XCTAssertEqual(request.httpMethod, "POST") 17 | XCTAssertEqual(request.path, "tokens") 18 | XCTAssertEqual(request.bodyParameters?["card"], "token") 19 | XCTAssertNil(request.queryParameters) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Tests/Networking/Requests/GetAcceptedBrandsTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GetAcceptedBrandsTests.swift 3 | // PAYJPTests 4 | // 5 | // Created by Li-Hsuan Chen on 2019/07/25. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import PAYJP 11 | 12 | class GetAcceptedBrandsTests: XCTestCase { 13 | func testInitializationWithTenantId() { 14 | let request = GetAcceptedBrands(tenantId: "mock_id") 15 | 16 | XCTAssertEqual(request.httpMethod, "GET") 17 | XCTAssertEqual(request.path, "accounts/brands") 18 | XCTAssertEqual(request.queryParameters?["tenant"] as? String, "mock_id") 19 | } 20 | 21 | func testInitializationWithoutTenantId() { 22 | let request = GetAcceptedBrands(tenantId: nil) 23 | 24 | XCTAssertEqual(request.httpMethod, "GET") 25 | XCTAssertEqual(request.path, "accounts/brands") 26 | XCTAssertNil(request.queryParameters?["tenant"]) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Tests/Networking/Requests/GetTokenRequestTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GetTokenRequestTests.swift 3 | // PAYJPTests 4 | // 5 | // Created by Li-Hsuan Chen on 2019/07/25. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import PAYJP 11 | 12 | class GetTokenRequestTests: XCTestCase { 13 | func testInitialization() { 14 | let request = GetTokenRequest(tokenId: "mock_id") 15 | 16 | XCTAssertEqual(request.httpMethod, "GET") 17 | XCTAssertEqual(request.path, "tokens/mock_id") 18 | 19 | XCTAssertNil(request.bodyParameters) 20 | XCTAssertNil(request.queryParameters) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Tests/PAYJPTests-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | -------------------------------------------------------------------------------- /Tests/PAYJPTests.xctestplan: -------------------------------------------------------------------------------- 1 | { 2 | "configurations" : [ 3 | { 4 | "id" : "34338B67-6AB8-4364-B59C-164A6AEBF39F", 5 | "name" : "Test Scheme Action", 6 | "options" : { 7 | 8 | } 9 | } 10 | ], 11 | "defaultOptions" : { 12 | "language" : "en" 13 | }, 14 | "testTargets" : [ 15 | { 16 | "parallelizable" : true, 17 | "target" : { 18 | "containerPath" : "container:PAYJP.xcodeproj", 19 | "identifier" : "615AFF641C8C74A1003FB86F", 20 | "name" : "PAYJPTests" 21 | } 22 | } 23 | ], 24 | "version" : 1 25 | } 26 | -------------------------------------------------------------------------------- /Tests/String+PAYJPTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // String+PAYJPTests.swift 3 | // PAYJPTests 4 | // 5 | // Created by Tadashi Wakayanagi on 2019/07/31. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import PAYJP 11 | 12 | // swiftlint:disable type_name 13 | class String_PAYJPTests: XCTestCase { 14 | 15 | private func testNumberfy(cases: [(String, String)]) { 16 | for (input, expected) in cases { 17 | let filtered = input.numberfy() 18 | XCTAssertEqual(filtered, expected) 19 | } 20 | } 21 | 22 | func testStringToDigitString() { 23 | let cases = [ 24 | ("", ""), 25 | ("12", "12"), 26 | ("1234 567", "1234567"), 27 | ("1234 5678 90", "1234567890"), 28 | ("1 @ 234 # 5", "12345") 29 | ] 30 | testNumberfy(cases: cases) 31 | } 32 | 33 | func testLocalizeString() { 34 | XCTAssertEqual("Please enter a card number.", "payjp_card_form_error_no_number".localized) 35 | } 36 | 37 | func testIsDigistOnly() { 38 | XCTAssertTrue("1".isDigitsOnly) 39 | XCTAssertTrue("1234".isDigitsOnly) 40 | XCTAssertFalse("a1".isDigitsOnly) 41 | XCTAssertFalse("abc".isDigitsOnly) 42 | } 43 | 44 | func testCapture() { 45 | let pattern = "^(https?)://([^/]+)/?" 46 | let target = "https://pay.jp/" 47 | 48 | let result = target.capture(pattern: pattern, group: 2) 49 | XCTAssertEqual(result, "pay.jp") 50 | } 51 | 52 | func testCapture_nil() { 53 | let pattern = "^(https?)://([^/]+)/?" 54 | let target = "pay.jp" 55 | 56 | let result = target.capture(pattern: pattern, group: 1) 57 | XCTAssertNil(result) 58 | } 59 | 60 | func testCaptureMulti() { 61 | let pattern = "^(https?)://([^/]+)/?" 62 | let target = "https://pay.jp/" 63 | 64 | let result = target.capture(pattern: pattern, group: [1, 2]) 65 | XCTAssertEqual(result[0], "https") 66 | XCTAssertEqual(result[1], "pay.jp") 67 | } 68 | 69 | func testCaptureMulti_empty() { 70 | let pattern = "^(https?)://([^/]+)/?" 71 | let target = "pay.jp" 72 | 73 | let result = target.capture(pattern: pattern, group: [1, 2]) 74 | XCTAssertTrue(result.isEmpty) 75 | } 76 | } 77 | // swiftlint:enable type_name 78 | -------------------------------------------------------------------------------- /Tests/StringProtocol+PAYJPTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StringProtocol+PAYJPTests.swift 3 | // PAYJPTests 4 | // 5 | // Created by Tadashi Wakayanagi on 2019/09/02. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import PAYJP 11 | 12 | // swiftlint:disable type_name 13 | class StringProtocol_PAYJPTests: XCTestCase { 14 | 15 | func testInsertSpaceWithCount() { 16 | var target = "1234567812345678" 17 | target.insert(separator: " ", every: 4) 18 | XCTAssertEqual(target, "1234 5678 1234 5678") 19 | } 20 | 21 | func testInsertSpaceWithPositions() { 22 | var target = "1234567812345678" 23 | target.insert(separator: " ", positions: [2, 4, 8, 14]) 24 | XCTAssertEqual(target, "12 34 5678 123456 78") 25 | } 26 | 27 | func testInsertSlashWithCount() { 28 | var target = "1234" 29 | target.insert(separator: "/", every: 2) 30 | XCTAssertEqual(target, "12/34") 31 | } 32 | } 33 | // swiftlint:enable type_name 34 | -------------------------------------------------------------------------------- /Tests/StubPaymentToken.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StubPaymentToken.swift 3 | // PAYJP 4 | // 5 | // Created by Li-Hsuan Chen on 2019/07/31. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import PassKit 11 | 12 | class StubPaymentToken: PKPaymentToken { 13 | // swiftlint:disable force_try 14 | override var paymentData: Data { 15 | let data = TestFixture.JSON(by: "paymentData.json") 16 | let json = try! JSONSerialization.jsonObject(with: data, options: .allowFragments) 17 | return try! JSONSerialization.data(withJSONObject: json, options: .prettyPrinted) 18 | } 19 | // swiftlint:enable force_try 20 | } 21 | -------------------------------------------------------------------------------- /Tests/TestFixture.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestHelper.swift 3 | // PAYJP 4 | // 5 | // Created by k@binc.jp on 2017/01/05. 6 | // Copyright © 2017 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct TestFixture { 12 | private static let bundle = Bundle(identifier: "jp.pay.ios.PAYJPTests")! 13 | 14 | static func JSON(by name: String) -> Data { 15 | let url = self.bundle.url(forResource: name, withExtension: nil, subdirectory: "Fixtures", localization: nil) 16 | // swiftlint:disable force_try 17 | let data = try! Data(contentsOf: url!) 18 | // swiftlint:enable force_try 19 | return data 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Tests/ThreeDSecure/Mock.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Mock.swift 3 | // PAYJP 4 | // 5 | // Created by Tadashi Wakayanagi on 2020/04/06. 6 | // Copyright © 2020 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import SafariServices 10 | 11 | class MockViewController: UIViewController { 12 | var tdsStatus: ThreeDSecureProcessStatus? 13 | var presentedVC: UIViewController? 14 | 15 | override func present(_ viewControllerToPresent: UIViewController, 16 | animated flag: Bool, 17 | completion: (() -> Void)? = nil) { 18 | presentedVC = viewControllerToPresent 19 | } 20 | } 21 | 22 | extension MockViewController: ThreeDSecureProcessHandlerDelegate { 23 | func threeDSecureProcessHandlerDidFinish(_ handler: ThreeDSecureProcessHandler, 24 | status: ThreeDSecureProcessStatus) { 25 | tdsStatus = status 26 | } 27 | } 28 | 29 | class MockSafariViewController: SFSafariViewController { 30 | var dismissCalled: Bool = false 31 | 32 | override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) { 33 | dismissCalled = true 34 | completion?() 35 | } 36 | } 37 | 38 | class MockThreeDSecureWebDriverDelegate: NSObject, ThreeDSecureWebDriverDelegate { 39 | var webBrowseDidFinishCalled: Bool = false 40 | 41 | func webBrowseDidFinish(_ driver: ThreeDSecureWebDriver) { 42 | webBrowseDidFinishCalled = true 43 | } 44 | } 45 | 46 | class MockWebDriver: ThreeDSecureWebDriver { 47 | var openWebBrowserUrl: URL? 48 | var isSafariVC: Bool = false 49 | 50 | init(isSafariVC: Bool = false) { 51 | self.isSafariVC = isSafariVC 52 | } 53 | 54 | func openWebBrowser(host: UIViewController, url: URL, delegate: ThreeDSecureWebDriverDelegate) { 55 | openWebBrowserUrl = url 56 | } 57 | 58 | func closeWebBrowser(host: UIViewController?, completion: (() -> Void)?) -> Bool { 59 | if isSafariVC { 60 | completion?() 61 | return true 62 | } 63 | return false 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Tests/ThreeDSecure/ThreeDSecureSFSafariViewControllerDriverTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ThreeDSecureSFSafariViewControllerDriverTests.swift 3 | // PAYJPTests 4 | // 5 | // Created by Tadashi Wakayanagi on 2020/04/06. 6 | // Copyright © 2020 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import SafariServices 11 | @testable import PAYJP 12 | 13 | class ThreeDSecureSFSafariViewControllerDriverTests: XCTestCase { 14 | 15 | func testOpenWebBrowser() { 16 | let driver = ThreeDSecureSFSafariViewControllerDriver() 17 | let mockVC = MockViewController() 18 | let mockDelegate = MockThreeDSecureWebDriverDelegate() 19 | let url = URL(string: "https://test")! 20 | 21 | driver.openWebBrowser(host: mockVC, url: url, delegate: mockDelegate) 22 | 23 | XCTAssertTrue(mockVC.presentedVC is SFSafariViewController) 24 | } 25 | 26 | func testCloseWebBrowser() { 27 | let driver = ThreeDSecureSFSafariViewControllerDriver() 28 | let mockVC = MockSafariViewController(url: URL(string: "https://test")!) 29 | 30 | let result = driver.closeWebBrowser(host: mockVC, completion: nil) 31 | 32 | XCTAssertTrue(result) 33 | XCTAssertTrue(mockVC.dismissCalled) 34 | } 35 | 36 | func testCloseWebBrowser_notSafariVC() { 37 | let driver = ThreeDSecureSFSafariViewControllerDriver() 38 | let mockVC = MockViewController() 39 | 40 | let result = driver.closeWebBrowser(host: mockVC, completion: nil) 41 | 42 | XCTAssertFalse(result) 43 | } 44 | 45 | func testWebBrowswDidFinishDelegate() { 46 | let driver = ThreeDSecureSFSafariViewControllerDriver() 47 | let mockVC = MockViewController() 48 | let mockDelegate = MockThreeDSecureWebDriverDelegate() 49 | let url = URL(string: "https://test")! 50 | 51 | driver.openWebBrowser(host: mockVC, url: url, delegate: mockDelegate) 52 | // SafariVCを閉じた場合を想定 53 | driver.safariViewControllerDidFinish(MockSafariViewController(url: url)) 54 | 55 | XCTAssertTrue(mockDelegate.webBrowseDidFinishCalled) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Tests/ThreeDSecure/ThreeDSecureURLConfigurationTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import PAYJP 3 | 4 | class ThreeDSecureURLConfigurationTests: XCTestCase { 5 | override func setUp() { 6 | super.setUp() 7 | 8 | PAYJPSDK.publicKey = "public_key" 9 | } 10 | 11 | func testMakeThreeDSecureEntryURL() { 12 | let redirectURL = URL(string: "test://")! 13 | let redirectURLKey = "test" 14 | let configuration = ThreeDSecureURLConfiguration(redirectURL: redirectURL, redirectURLKey: redirectURLKey) 15 | let resourceId = "ch_123" 16 | let expectedUrlString = "\(PAYJPApiEndpoint)tds/\(resourceId)/start?publickey=\(PAYJPSDK.publicKey!)&back=\(redirectURLKey)" 17 | let threeDSecureEntryURL = configuration.makeThreeDSecureEntryURL(resourceId: resourceId) 18 | 19 | XCTAssertEqual(threeDSecureEntryURL.absoluteString, expectedUrlString) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Tests/ThreeDSecure/ThreeDSecureWebViewControllerDriverTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import PAYJP 3 | 4 | final class ThreeDSecureWebViewControllerDriverTests: XCTestCase { 5 | class DummyHostViewController: UIViewController { 6 | var presentedVC: UIViewController? 7 | override func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) { 8 | presentedVC = viewControllerToPresent 9 | completion?() 10 | } 11 | } 12 | 13 | class DummyDelegate: ThreeDSecureWebDriverDelegate { 14 | var didFinishCalled = false 15 | func webBrowseDidFinish(_ driver: ThreeDSecureWebDriver) { 16 | didFinishCalled = true 17 | } 18 | } 19 | 20 | func testOpenWebBrowser_presentsWebViewController() { 21 | let driver = ThreeDSecureWebViewControllerDriver() 22 | let host = DummyHostViewController() 23 | let delegate = DummyDelegate() 24 | let url = URL(string: "https://example.com")! 25 | 26 | driver.openWebBrowser(host: host, url: url, delegate: delegate) 27 | XCTAssertTrue(host.presentedVC is ThreeDSecureWebViewController) 28 | } 29 | 30 | func testCloseWebBrowser_dismissesWebViewController() { 31 | let driver = ThreeDSecureWebViewControllerDriver() 32 | let host = DummyHostViewController() 33 | let delegate = DummyDelegate() 34 | let url = URL(string: "https://example.com")! 35 | driver.openWebBrowser(host: host, url: url, delegate: delegate) 36 | let result = driver.closeWebBrowser(host: nil, completion: nil) 37 | XCTAssertTrue(result) 38 | } 39 | 40 | func testWebViewControllerDidFinish_callsDelegate() { 41 | let driver = ThreeDSecureWebViewControllerDriver() 42 | let host = DummyHostViewController() 43 | let delegate = DummyDelegate() 44 | let url = URL(string: "https://example.com")! 45 | driver.openWebBrowser(host: host, url: url, delegate: delegate) 46 | if let webVC = host.presentedVC as? ThreeDSecureWebViewController { 47 | (driver as? ThreeDSecureWebViewControllerDriver)?.webViewControllerDidFinish(webVC, completed: false) 48 | XCTAssertTrue(delegate.didFinishCalled) 49 | } else { 50 | XCTFail("WebViewController was not presented") 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Tests/ThreeDSecure/ThreeDSecureWebViewControllerTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import WebKit 3 | @testable import PAYJP 4 | 5 | final class ThreeDSecureWebViewControllerTests: XCTestCase { 6 | func testUserAgentIsSetCorrectly() { 7 | let version = Bundle.payjpBundle.infoDictionary?["CFBundleShortVersionString"] as? String ?? "" 8 | let url = URL(string: "https://example.com")! 9 | let vc = ThreeDSecureWebViewController(url: url) 10 | let exp = expectation(description: "Retrieve UserAgent") 11 | 12 | _ = vc.view 13 | 14 | let webViewMirror = Mirror(reflecting: vc) 15 | guard let webView = webViewMirror.children.first(where: { $0.label == "webView" })?.value as? WKWebView else { 16 | XCTFail("Failed to retrieve webView") 17 | return 18 | } 19 | webView.evaluateJavaScript("navigator.userAgent") { result, error in 20 | guard let userAgent = result as? String else { 21 | XCTFail("Failed to retrieve UserAgent: \(error?.localizedDescription ?? "")") 22 | exp.fulfill() 23 | return 24 | } 25 | XCTAssertTrue(userAgent.contains("PAY.JP iOS WKWebView/\(version)"), "Expected value is not included in UserAgent: \(userAgent)") 26 | exp.fulfill() 27 | } 28 | wait(for: [exp], timeout: 5.0) 29 | } 30 | 31 | func testErrorIsDisplayedInWebView() { 32 | let url = URL(string: "http://invalid.invalid-domain-for-test-404.localhost/")! 33 | let vc = ThreeDSecureWebViewController(url: url) 34 | let exp = expectation(description: "Display error HTML") 35 | 36 | _ = vc.view 37 | 38 | let webViewMirror = Mirror(reflecting: vc) 39 | guard let webView = webViewMirror.children.first(where: { $0.label == "webView" })?.value as? WKWebView else { 40 | XCTFail("Failed to retrieve webView") 41 | return 42 | } 43 | 44 | DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { 45 | webView.evaluateJavaScript("document.body.innerHTML") { result, error in 46 | guard let html = result as? String else { 47 | XCTFail("Failed to retrieve HTML: \(error?.localizedDescription ?? "")") 48 | exp.fulfill() 49 | return 50 | } 51 | XCTAssertTrue(html.contains("A server with the specified hostname could not be found."), "Error content is not included in the HTML: \(html)") 52 | exp.fulfill() 53 | } 54 | } 55 | wait(for: [exp], timeout: 10.0) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Tests/Validators/CardHolderValidatorTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CardHolderValidatorTests.swift 3 | // PAYJP 4 | // 5 | // Created by Tatsuya Kitagawa on 2024/10/02. 6 | // Copyright © 2024 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import PAYJP 11 | 12 | class CardHolderValidatorTests: XCTestCase { 13 | func testCardHolderValidation() { 14 | let validator = CardHolderValidator() 15 | XCTAssertEqual(validator.validate(cardHolder: "JANE DOE"), .valid) 16 | XCTAssertEqual(validator.validate(cardHolder: "abcABC012 -."), .valid) 17 | XCTAssertEqual(validator.validate(cardHolder: "山田たろう"), .invalidCardHolderCharacter) 18 | // 全角スペースは不可 19 | XCTAssertEqual(validator.validate(cardHolder: "JANE DOE"), .invalidCardHolderCharacter) 20 | // 全角数字は不可 21 | XCTAssertEqual(validator.validate(cardHolder: "123"), .invalidCardHolderCharacter) 22 | // 46文字以上は不可 23 | XCTAssertEqual(validator.validate(cardHolder: "1234567890123456789012345678901234567890123456"), .invalidCardHolderLength) 24 | // 45文字はOK 25 | XCTAssertEqual(validator.validate(cardHolder: "123456789012345678901234567890123456789012345"), .valid) 26 | // 1文字は不可 27 | XCTAssertEqual(validator.validate(cardHolder: "1"), .invalidCardHolderLength) 28 | // 2文字はOK 29 | XCTAssertEqual(validator.validate(cardHolder: "12"), .valid) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Tests/Validators/CardNumberValidatorTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CardNumberValidatorTests.swift 3 | // PAYJPTests 4 | // 5 | // Created by Tadashi Wakayanagi on 2019/07/19. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import PAYJP 11 | 12 | class CardNumberValidatorTests: XCTestCase { 13 | 14 | let validator = CardNumberValidator() 15 | 16 | private func testIsValid(cases: [(String, CardBrand, Bool)]) { 17 | for (number, brand, expected) in cases { 18 | let valid = validator.isValid(cardNumber: number, brand: brand) 19 | XCTAssertEqual(valid, expected) 20 | } 21 | } 22 | 23 | func testCardNumberValidation() { 24 | let cases: [(String, CardBrand, Bool)] = [ 25 | // 13桁 26 | ("1234567890123", .unknown, false), 27 | // luhn NG 28 | ("12345678901234", .unknown, false), 29 | // luhn OK 30 | ("12345678901237", .unknown, false), 31 | ("4242424242424242", .unknown, true), 32 | ("12345678901234569", .unknown, false), 33 | ("12345678901237ab", .unknown, false), 34 | ("ab12345678901237", .unknown, false), 35 | // Visa 36 | ("4200250796648831", .visa, true), 37 | ("4929613427952262", .visa, true), 38 | ("4929610527143692", .visa, false), 39 | // Master 40 | ("5269278488737492", .mastercard, true), 41 | ("5106733522040110", .mastercard, true), 42 | ("5589306849102132", .mastercard, false), 43 | // JCB 44 | ("3533401879982122", .jcb, true), 45 | ("3535909680226735", .jcb, true), 46 | ("3534067821171002", .jcb, false), 47 | // Amex 48 | ("346191816620108", .americanExpress, true), 49 | ("341179142096577", .americanExpress, true), 50 | ("372086951160373", .americanExpress, false), 51 | // Discover 52 | ("6011341651562441", .discover, true), 53 | ("6011290763638088", .discover, true), 54 | ("6011621030885715", .discover, false), 55 | // Diners 56 | ("36868003801279", .dinersClub, true), 57 | ("36785415704877", .dinersClub, true), 58 | ("36267608413862", .dinersClub, false) 59 | ] 60 | testIsValid(cases: cases) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Tests/Validators/CvcValidatorTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CvcValidatorTests.swift 3 | // PAYJPTests 4 | // 5 | // Created by Tadashi Wakayanagi on 2019/07/19. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import PAYJP 11 | 12 | class CvcValidatorTests: XCTestCase { 13 | 14 | let validator = CvcValidator() 15 | 16 | func testIsValid(cases: [(String, CardBrand, (Bool, Bool))]) { 17 | for (cvc, brand, expected) in cases { 18 | let result = validator.isValid(cvc: cvc, brand: brand) 19 | XCTAssertEqual(result.validated, expected.0) 20 | XCTAssertEqual(result.isInstant, expected.1) 21 | } 22 | } 23 | 24 | func testCvcValidation() { 25 | let cases: [(String, CardBrand, (Bool, Bool))] = [ 26 | ("", .unknown, (false, false)), 27 | ("", .visa, (false, false)), 28 | ("", .americanExpress, (false, false)), 29 | ("1", .unknown, (false, false)), 30 | ("1", .mastercard, (false, false)), 31 | ("1", .americanExpress, (false, false)), 32 | ("12", .unknown, (false, false)), 33 | ("12", .jcb, (false, false)), 34 | ("12", .americanExpress, (false, false)), 35 | ("123", .unknown, (false, false)), 36 | ("123", .dinersClub, (true, false)), 37 | ("123", .americanExpress, (false, false)), 38 | ("1234", .unknown, (true, false)), 39 | ("1234", .discover, (false, true)), 40 | ("1234", .americanExpress, (true, false)), 41 | ("12345", .unknown, (false, true)), 42 | ("12345", .visa, (false, true)), 43 | ("12345", .americanExpress, (false, true)), 44 | ("a123", .unknown, (false, false)), 45 | ("a123", .mastercard, (false, false)), 46 | ("a123", .americanExpress, (false, false)), 47 | ("123a", .unknown, (false, false)), 48 | ("123a", .jcb, (false, false)), 49 | ("123a", .americanExpress, (false, false)) 50 | ] 51 | testIsValid(cases: cases) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Tests/Validators/ExpirationValidatorTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExpirationValidatorTests.swift 3 | // PAYJPTests 4 | // 5 | // Created by Li-Hsuan Chen on 2019/07/19. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import PAYJP 11 | 12 | class ExpirationValidatorTests: XCTestCase { 13 | func testCurrentDate() { 14 | let date = Date(timeIntervalSinceNow: 0) 15 | let (month, year) = extractMonthYear(date: date) 16 | 17 | let validator = ExpirationValidator() 18 | let isValid = validator.isValid(month: month, year: year) 19 | 20 | XCTAssertTrue(isValid) 21 | } 22 | 23 | func testPastDate() { 24 | let date = Date(timeIntervalSinceNow: -60 * 60 * 24 * 30 * 2) // 今から二ヶ月くらい前 25 | let (month, year) = extractMonthYear(date: date) 26 | 27 | let validator = ExpirationValidator() 28 | let isValid = validator.isValid(month: month, year: year) 29 | 30 | XCTAssertFalse(isValid) 31 | } 32 | 33 | func testFutureDate() { 34 | let date = Date(timeIntervalSinceNow: 60 * 60 * 24 * 30 * 2) // 今から二ヶ月くらい後 35 | let (month, year) = extractMonthYear(date: date) 36 | 37 | let validator = ExpirationValidator() 38 | let isValid = validator.isValid(month: month, year: year) 39 | 40 | XCTAssertTrue(isValid) 41 | } 42 | 43 | private func extractMonthYear(date: Date) -> (month: String, year: String) { 44 | let calendar = Calendar(identifier: .gregorian) 45 | let year = calendar.component(.year, from: date) % 100 46 | let month = calendar.component(.month, from: date) 47 | return ("\(month)", "\(year)") 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Tests/Validators/PublicKeyValidatorTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PublicKeyValidatorTests.swift 3 | // PAYJPTests 4 | // 5 | // Created by Tadashi Wakayanagi on 2019/11/27. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import PAYJP 11 | 12 | class PublicKeyValidatorTests: XCTestCase { 13 | 14 | func testPublicKeyValidationSuccess() { 15 | let cases: [String] = [ 16 | ("pk_test_123456789"), 17 | ("pk_live_123456789") 18 | ] 19 | 20 | for publicKey in cases { 21 | var results: [Bool] = [] 22 | let assert: (Bool, String, StaticString, UInt) -> Void = { condition, _, _, _ in 23 | results.append(condition) 24 | } 25 | let validator = PublicKeyValidator(assert: assert) 26 | validator.validate(publicKey: publicKey) 27 | // assertの成功をチェックするため、conditionにfalseが含まれていないことをテストする 28 | XCTAssertFalse(results.contains(false)) 29 | } 30 | } 31 | 32 | func testPublicKeyValidationFailed() { 33 | let cases: [(String, String)] = [ 34 | ("", "❌You need to set publickey for PAY.JP. You can find in https://pay.jp/d/settings ."), 35 | (" ", "❌You need to set publickey for PAY.JP. You can find in https://pay.jp/d/settings ."), 36 | ("sk_test_123456789", "❌You are using secretkey (`sk_xxxx`) instead of PAY.JP publickey." + 37 | "You can find **public** key like `pk_xxxxxx` in https://pay.jp/d/settings ."), 38 | ("sk_live_123456789", "❌You are using secretkey (`sk_xxxx`) instead of PAY.JP publickey." + 39 | "You can find **public** key like `pk_xxxxxx` in https://pay.jp/d/settings .") 40 | ] 41 | 42 | for (publicKey, expectedMessage) in cases { 43 | var results: [(Bool, String)] = [] 44 | let assert: (Bool, String, StaticString, UInt) -> Void = { condition, message, _, _ in 45 | results.append((condition, message)) 46 | } 47 | let validator = PublicKeyValidator(assert: assert) 48 | validator.validate(publicKey: publicKey) 49 | // assertの失敗をチェックするため、conditionにfalseが含まれていることをテストする 50 | XCTAssertTrue(results.contains(where: { (condition, message) -> Bool in 51 | return !condition && message == expectedMessage 52 | })) 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /docs/docsets/PAYJP.docset/Contents/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleIdentifier 6 | com.jazzy.payjp 7 | CFBundleName 8 | PAYJP 9 | DocSetPlatformFamily 10 | payjp 11 | isDashDocset 12 | 13 | dashIndexFilePath 14 | index.html 15 | isJavaScriptEnabled 16 | 17 | DashDocSetFamily 18 | dashtoc 19 | 20 | 21 | -------------------------------------------------------------------------------- /docs/docsets/PAYJP.docset/Contents/Resources/Documents/img/carat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/payjp/payjp-ios/d4e228ba0bfc6dd82b216342a57c03059043ace7/docs/docsets/PAYJP.docset/Contents/Resources/Documents/img/carat.png -------------------------------------------------------------------------------- /docs/docsets/PAYJP.docset/Contents/Resources/Documents/img/dash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/payjp/payjp-ios/d4e228ba0bfc6dd82b216342a57c03059043ace7/docs/docsets/PAYJP.docset/Contents/Resources/Documents/img/dash.png -------------------------------------------------------------------------------- /docs/docsets/PAYJP.docset/Contents/Resources/Documents/img/gh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/payjp/payjp-ios/d4e228ba0bfc6dd82b216342a57c03059043ace7/docs/docsets/PAYJP.docset/Contents/Resources/Documents/img/gh.png -------------------------------------------------------------------------------- /docs/docsets/PAYJP.docset/Contents/Resources/Documents/img/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/payjp/payjp-ios/d4e228ba0bfc6dd82b216342a57c03059043ace7/docs/docsets/PAYJP.docset/Contents/Resources/Documents/img/spinner.gif -------------------------------------------------------------------------------- /docs/docsets/PAYJP.docset/Contents/Resources/Documents/js/jazzy.js: -------------------------------------------------------------------------------- 1 | // Jazzy - https://github.com/realm/jazzy 2 | // Copyright Realm Inc. 3 | // SPDX-License-Identifier: MIT 4 | 5 | window.jazzy = {'docset': false} 6 | if (typeof window.dash != 'undefined') { 7 | document.documentElement.className += ' dash' 8 | window.jazzy.docset = true 9 | } 10 | if (navigator.userAgent.match(/xcode/i)) { 11 | document.documentElement.className += ' xcode' 12 | window.jazzy.docset = true 13 | } 14 | 15 | function toggleItem($link, $content) { 16 | var animationDuration = 300; 17 | $link.toggleClass('token-open'); 18 | $content.slideToggle(animationDuration); 19 | } 20 | 21 | function itemLinkToContent($link) { 22 | return $link.parent().parent().next(); 23 | } 24 | 25 | // On doc load + hash-change, open any targetted item 26 | function openCurrentItemIfClosed() { 27 | if (window.jazzy.docset) { 28 | return; 29 | } 30 | var $link = $(`a[name="${location.hash.substring(1)}"]`).nextAll('.token'); 31 | $content = itemLinkToContent($link); 32 | if ($content.is(':hidden')) { 33 | toggleItem($link, $content); 34 | } 35 | } 36 | 37 | $(openCurrentItemIfClosed); 38 | $(window).on('hashchange', openCurrentItemIfClosed); 39 | 40 | // On item link ('token') click, toggle its discussion 41 | $('.token').on('click', function(event) { 42 | if (window.jazzy.docset) { 43 | return; 44 | } 45 | var $link = $(this); 46 | toggleItem($link, itemLinkToContent($link)); 47 | 48 | // Keeps the document from jumping to the hash. 49 | var href = $link.attr('href'); 50 | if (history.pushState) { 51 | history.pushState({}, '', href); 52 | } else { 53 | location.hash = href; 54 | } 55 | event.preventDefault(); 56 | }); 57 | 58 | // Clicks on links to the current, closed, item need to open the item 59 | $("a:not('.token')").on('click', function() { 60 | if (location == this.href) { 61 | openCurrentItemIfClosed(); 62 | } 63 | }); 64 | 65 | // KaTeX rendering 66 | if ("katex" in window) { 67 | $($('.math').each( (_, element) => { 68 | katex.render(element.textContent, element, { 69 | displayMode: $(element).hasClass('m-block'), 70 | throwOnError: false, 71 | trust: true 72 | }); 73 | })) 74 | } 75 | -------------------------------------------------------------------------------- /docs/docsets/PAYJP.docset/Contents/Resources/Documents/js/jazzy.search.js: -------------------------------------------------------------------------------- 1 | // Jazzy - https://github.com/realm/jazzy 2 | // Copyright Realm Inc. 3 | // SPDX-License-Identifier: MIT 4 | 5 | $(function(){ 6 | var $typeahead = $('[data-typeahead]'); 7 | var $form = $typeahead.parents('form'); 8 | var searchURL = $form.attr('action'); 9 | 10 | function displayTemplate(result) { 11 | return result.name; 12 | } 13 | 14 | function suggestionTemplate(result) { 15 | var t = '
'; 16 | t += '' + result.name + ''; 17 | if (result.parent_name) { 18 | t += '' + result.parent_name + ''; 19 | } 20 | t += '
'; 21 | return t; 22 | } 23 | 24 | $typeahead.one('focus', function() { 25 | $form.addClass('loading'); 26 | 27 | $.getJSON(searchURL).then(function(searchData) { 28 | const searchIndex = lunr(function() { 29 | this.ref('url'); 30 | this.field('name'); 31 | this.field('abstract'); 32 | for (const [url, doc] of Object.entries(searchData)) { 33 | this.add({url: url, name: doc.name, abstract: doc.abstract}); 34 | } 35 | }); 36 | 37 | $typeahead.typeahead( 38 | { 39 | highlight: true, 40 | minLength: 3, 41 | autoselect: true 42 | }, 43 | { 44 | limit: 10, 45 | display: displayTemplate, 46 | templates: { suggestion: suggestionTemplate }, 47 | source: function(query, sync) { 48 | const lcSearch = query.toLowerCase(); 49 | const results = searchIndex.query(function(q) { 50 | q.term(lcSearch, { boost: 100 }); 51 | q.term(lcSearch, { 52 | boost: 10, 53 | wildcard: lunr.Query.wildcard.TRAILING 54 | }); 55 | }).map(function(result) { 56 | var doc = searchData[result.ref]; 57 | doc.url = result.ref; 58 | return doc; 59 | }); 60 | sync(results); 61 | } 62 | } 63 | ); 64 | $form.removeClass('loading'); 65 | $typeahead.trigger('focus'); 66 | }); 67 | }); 68 | 69 | var baseURL = searchURL.slice(0, -"search.json".length); 70 | 71 | $typeahead.on('typeahead:select', function(e, result) { 72 | window.location = baseURL + result.url; 73 | }); 74 | }); 75 | -------------------------------------------------------------------------------- /docs/docsets/PAYJP.docset/Contents/Resources/docSet.dsidx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/payjp/payjp-ios/d4e228ba0bfc6dd82b216342a57c03059043ace7/docs/docsets/PAYJP.docset/Contents/Resources/docSet.dsidx -------------------------------------------------------------------------------- /docs/img/carat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/payjp/payjp-ios/d4e228ba0bfc6dd82b216342a57c03059043ace7/docs/img/carat.png -------------------------------------------------------------------------------- /docs/img/dash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/payjp/payjp-ios/d4e228ba0bfc6dd82b216342a57c03059043ace7/docs/img/dash.png -------------------------------------------------------------------------------- /docs/img/gh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/payjp/payjp-ios/d4e228ba0bfc6dd82b216342a57c03059043ace7/docs/img/gh.png -------------------------------------------------------------------------------- /docs/img/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/payjp/payjp-ios/d4e228ba0bfc6dd82b216342a57c03059043ace7/docs/img/spinner.gif -------------------------------------------------------------------------------- /docs/js/jazzy.js: -------------------------------------------------------------------------------- 1 | // Jazzy - https://github.com/realm/jazzy 2 | // Copyright Realm Inc. 3 | // SPDX-License-Identifier: MIT 4 | 5 | window.jazzy = {'docset': false} 6 | if (typeof window.dash != 'undefined') { 7 | document.documentElement.className += ' dash' 8 | window.jazzy.docset = true 9 | } 10 | if (navigator.userAgent.match(/xcode/i)) { 11 | document.documentElement.className += ' xcode' 12 | window.jazzy.docset = true 13 | } 14 | 15 | function toggleItem($link, $content) { 16 | var animationDuration = 300; 17 | $link.toggleClass('token-open'); 18 | $content.slideToggle(animationDuration); 19 | } 20 | 21 | function itemLinkToContent($link) { 22 | return $link.parent().parent().next(); 23 | } 24 | 25 | // On doc load + hash-change, open any targetted item 26 | function openCurrentItemIfClosed() { 27 | if (window.jazzy.docset) { 28 | return; 29 | } 30 | var $link = $(`a[name="${location.hash.substring(1)}"]`).nextAll('.token'); 31 | $content = itemLinkToContent($link); 32 | if ($content.is(':hidden')) { 33 | toggleItem($link, $content); 34 | } 35 | } 36 | 37 | $(openCurrentItemIfClosed); 38 | $(window).on('hashchange', openCurrentItemIfClosed); 39 | 40 | // On item link ('token') click, toggle its discussion 41 | $('.token').on('click', function(event) { 42 | if (window.jazzy.docset) { 43 | return; 44 | } 45 | var $link = $(this); 46 | toggleItem($link, itemLinkToContent($link)); 47 | 48 | // Keeps the document from jumping to the hash. 49 | var href = $link.attr('href'); 50 | if (history.pushState) { 51 | history.pushState({}, '', href); 52 | } else { 53 | location.hash = href; 54 | } 55 | event.preventDefault(); 56 | }); 57 | 58 | // Clicks on links to the current, closed, item need to open the item 59 | $("a:not('.token')").on('click', function() { 60 | if (location == this.href) { 61 | openCurrentItemIfClosed(); 62 | } 63 | }); 64 | 65 | // KaTeX rendering 66 | if ("katex" in window) { 67 | $($('.math').each( (_, element) => { 68 | katex.render(element.textContent, element, { 69 | displayMode: $(element).hasClass('m-block'), 70 | throwOnError: false, 71 | trust: true 72 | }); 73 | })) 74 | } 75 | -------------------------------------------------------------------------------- /docs/js/jazzy.search.js: -------------------------------------------------------------------------------- 1 | // Jazzy - https://github.com/realm/jazzy 2 | // Copyright Realm Inc. 3 | // SPDX-License-Identifier: MIT 4 | 5 | $(function(){ 6 | var $typeahead = $('[data-typeahead]'); 7 | var $form = $typeahead.parents('form'); 8 | var searchURL = $form.attr('action'); 9 | 10 | function displayTemplate(result) { 11 | return result.name; 12 | } 13 | 14 | function suggestionTemplate(result) { 15 | var t = '
'; 16 | t += '' + result.name + ''; 17 | if (result.parent_name) { 18 | t += '' + result.parent_name + ''; 19 | } 20 | t += '
'; 21 | return t; 22 | } 23 | 24 | $typeahead.one('focus', function() { 25 | $form.addClass('loading'); 26 | 27 | $.getJSON(searchURL).then(function(searchData) { 28 | const searchIndex = lunr(function() { 29 | this.ref('url'); 30 | this.field('name'); 31 | this.field('abstract'); 32 | for (const [url, doc] of Object.entries(searchData)) { 33 | this.add({url: url, name: doc.name, abstract: doc.abstract}); 34 | } 35 | }); 36 | 37 | $typeahead.typeahead( 38 | { 39 | highlight: true, 40 | minLength: 3, 41 | autoselect: true 42 | }, 43 | { 44 | limit: 10, 45 | display: displayTemplate, 46 | templates: { suggestion: suggestionTemplate }, 47 | source: function(query, sync) { 48 | const lcSearch = query.toLowerCase(); 49 | const results = searchIndex.query(function(q) { 50 | q.term(lcSearch, { boost: 100 }); 51 | q.term(lcSearch, { 52 | boost: 10, 53 | wildcard: lunr.Query.wildcard.TRAILING 54 | }); 55 | }).map(function(result) { 56 | var doc = searchData[result.ref]; 57 | doc.url = result.ref; 58 | return doc; 59 | }); 60 | sync(results); 61 | } 62 | } 63 | ); 64 | $form.removeClass('loading'); 65 | $typeahead.trigger('focus'); 66 | }); 67 | }); 68 | 69 | var baseURL = searchURL.slice(0, -"search.json".length); 70 | 71 | $typeahead.on('typeahead:select', function(e, result) { 72 | window.location = baseURL + result.url; 73 | }); 74 | }); 75 | -------------------------------------------------------------------------------- /example-objc/.gitignore: -------------------------------------------------------------------------------- 1 | Pods/ 2 | Podfile.lock 3 | -------------------------------------------------------------------------------- /example-objc/Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '12.0' 2 | use_frameworks! 3 | 4 | target 'example-objc' do 5 | # pod 'PAYJP' 6 | pod 'PAYJP', :path => '../' 7 | # NOTE: if you need to scan card, add CardIO dependency like below 8 | # pod 'CardIO' 9 | end 10 | -------------------------------------------------------------------------------- /example-objc/example-objc.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example-objc/example-objc.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example-objc/example-objc.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example-objc/example-objc/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // example-objc 4 | // 5 | // Created by Tatsuya Kitagawa on 2017/12/08. 6 | // Copyright © 2017年 PAY, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property(strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /example-objc/example-objc/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /example-objc/example-objc/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /example-objc/example-objc/CardFormViewExampleViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // CardFormViewExampleViewController.h 3 | // example-objc 4 | // 5 | // Created by Li-Hsuan Chen on 2019/07/23. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | @import PAYJP; 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface CardFormViewExampleViewController : UITableViewController 18 | 19 | @end 20 | 21 | NS_ASSUME_NONNULL_END 22 | -------------------------------------------------------------------------------- /example-objc/example-objc/CardFormViewScrollViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // CardFormViewScrollViewConstroller.h 3 | // example-objc 4 | // 5 | // Created by Tadashi Wakayanagi on 2019/08/28. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | @import PAYJP; 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface CardFormViewScrollViewController 15 | : UIViewController 20 | 21 | @end 22 | 23 | NS_ASSUME_NONNULL_END 24 | -------------------------------------------------------------------------------- /example-objc/example-objc/ColorTheme.h: -------------------------------------------------------------------------------- 1 | // 2 | // ColorStyle.h 3 | // example-objc 4 | // 5 | // Created by Tadashi Wakayanagi on 2019/09/25. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | #ifndef ColorStyle_h 10 | #define ColorStyle_h 11 | 12 | typedef NS_ENUM(NSInteger, ColorTheme) { Normal, Red, Blue, Dark }; 13 | #define GetColorThemeText(type) ColorThemeTextList[type] 14 | #define GetColorTheme(typeText) (ColorTheme)[ColorThemeTextList indexOfObject:typeText] 15 | #define ColorThemeTextList @[ @"Normal", @"Red", @"Blue", @"Dark" ] 16 | #define RGB(r, g, b) [UIColor colorWithRed:(r) / 255.0 green:(g) / 255.0 blue:(b) / 255.0 alpha:1] 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /example-objc/example-objc/ExampleHostViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ExampleHostViewController.h 3 | // example-objc 4 | // 5 | // Created by Tadashi Wakayanagi on 2019/11/18. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | @import PAYJP; 11 | 12 | @interface ExampleHostViewController : UITableViewController 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /example-objc/example-objc/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 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 | CFBundleURLTypes 20 | 21 | 22 | CFBundleURLName 23 | $(PRODUCT_BUNDLE_IDENTIFIER) 24 | CFBundleURLSchemes 25 | 26 | exampleobjc 27 | 28 | 29 | 30 | CFBundleVersion 31 | 1 32 | LSRequiresIPhoneOS 33 | 34 | NSCameraUsageDescription 35 | We will use camera for card scanning. 36 | UILaunchStoryboardName 37 | LaunchScreen 38 | UIMainStoryboardFile 39 | Main 40 | UIRequiredDeviceCapabilities 41 | 42 | armv7 43 | 44 | UISupportedInterfaceOrientations 45 | 46 | UIInterfaceOrientationPortrait 47 | UIInterfaceOrientationLandscapeLeft 48 | UIInterfaceOrientationLandscapeRight 49 | 50 | UISupportedInterfaceOrientations~ipad 51 | 52 | UIInterfaceOrientationPortrait 53 | UIInterfaceOrientationPortraitUpsideDown 54 | UIInterfaceOrientationLandscapeLeft 55 | UIInterfaceOrientationLandscapeRight 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /example-objc/example-objc/SampleService.h: -------------------------------------------------------------------------------- 1 | // 2 | // SampleService.h 3 | // example-objc 4 | // 5 | // Created by Tadashi Wakayanagi on 2019/11/25. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | @import PAYJP; 11 | 12 | @interface SampleService : NSObject 13 | 14 | + (SampleService *)sharedService; 15 | - (void)saveCardWithToken:(NSString *)token completion:(void (^)(NSError *error))completion; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /example-objc/example-objc/ThreeDSecureExampleViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ThreeDSecureExampleViewController.h 3 | // example-objc 4 | // 5 | 6 | #import 7 | @import PAYJP; 8 | 9 | NS_ASSUME_NONNULL_BEGIN 10 | 11 | @interface ThreeDSecureExampleViewController 12 | : UIViewController 13 | 14 | @end 15 | 16 | NS_ASSUME_NONNULL_END -------------------------------------------------------------------------------- /example-objc/example-objc/ThreeDSecureExampleViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ThreeDSecureExampleViewController.m 3 | // example-objc 4 | // 5 | 6 | #import "ThreeDSecureExampleViewController.h" 7 | #import "UIViewController+Alert.h" 8 | 9 | @interface ThreeDSecureExampleViewController () 10 | 11 | @property(weak, nonatomic) IBOutlet UIButton *startButton; 12 | @property(weak, nonatomic) IBOutlet UITextField *textField; 13 | @property(weak, nonatomic) IBOutlet UILabel *resultLabel; 14 | @property(strong, nonatomic) NSString *pendingResourceId; 15 | 16 | @end 17 | 18 | @implementation ThreeDSecureExampleViewController 19 | 20 | - (void)viewDidLoad { 21 | [super viewDidLoad]; 22 | } 23 | 24 | - (IBAction)startThreeDSecure:(id)sender { 25 | if (self.textField.text.length == 0) { 26 | self.resultLabel.text = @""; 27 | self.resultLabel.hidden = YES; 28 | return; 29 | } 30 | 31 | self.pendingResourceId = self.textField.text; 32 | [[PAYJPThreeDSecureProcessHandler sharedHandler] 33 | startThreeDSecureProcessWithViewController:self 34 | delegate:self 35 | resourceId:self.textField.text]; 36 | } 37 | 38 | #pragma mark - PAYJPThreeDSecureProcessHandlerDelegate 39 | 40 | - (void)threeDSecureProcessHandlerDidFinish:(PAYJPThreeDSecureProcessHandler *)handler 41 | status:(enum ThreeDSecureProcessStatus)status { 42 | switch (status) { 43 | case ThreeDSecureProcessStatusCompleted: 44 | self.resultLabel.text = 45 | @"3Dセキュア認証が終了しました。\nこの結果をサーバーサイドに伝え、完了処理や結果のハンド" 46 | @"リングを行なってください。\n後続処理の実装方法に関してはドキュメントをご参照ください。"; 47 | self.resultLabel.textColor = UIColor.blackColor; 48 | self.resultLabel.hidden = NO; 49 | break; 50 | case ThreeDSecureProcessStatusCanceled: 51 | self.resultLabel.text = @"3Dセキュア認証がキャンセルされました。"; 52 | self.resultLabel.textColor = UIColor.redColor; 53 | self.resultLabel.hidden = NO; 54 | break; 55 | default: 56 | break; 57 | } 58 | } 59 | 60 | @end 61 | -------------------------------------------------------------------------------- /example-objc/example-objc/UIViewController+Alert.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewController+Alert.h 3 | // example-objc 4 | // 5 | // Created by Tadashi Wakayanagi on 2019/11/20. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | @import PAYJP; 11 | 12 | @interface UIViewController (Alert) 13 | 14 | - (void)showToken:(PAYToken *)token; 15 | - (void)showError:(NSError *)error; 16 | - (NSString *)displayToken:(PAYToken *)token; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /example-objc/example-objc/UIViewController+Alert.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewController+Alert.m 3 | // example-objc 4 | // 5 | // Created by Tadashi Wakayanagi on 2019/11/20. 6 | // Copyright © 2019 PAY, Inc. All rights reserved. 7 | // 8 | 9 | #import "UIViewController+Alert.h" 10 | 11 | @implementation UIViewController (Alert) 12 | 13 | - (void)showToken:(PAYToken *)token { 14 | UIAlertController *alert = 15 | [UIAlertController alertControllerWithTitle:@"success" 16 | message:[self displayToken:token] 17 | preferredStyle:UIAlertControllerStyleAlert]; 18 | [alert addAction:[UIAlertAction actionWithTitle:@"OK" 19 | style:UIAlertActionStyleCancel 20 | handler:nil]]; 21 | [self presentViewController:alert animated:true completion:nil]; 22 | } 23 | 24 | - (void)showError:(NSError *)error { 25 | UIAlertController *alert = 26 | [UIAlertController alertControllerWithTitle:@"error" 27 | message:error.localizedDescription 28 | preferredStyle:UIAlertControllerStyleAlert]; 29 | [alert addAction:[UIAlertAction actionWithTitle:@"OK" 30 | style:UIAlertActionStyleCancel 31 | handler:nil]]; 32 | [self presentViewController:alert animated:true completion:nil]; 33 | } 34 | 35 | - (NSString *)displayToken:(PAYToken *)token { 36 | return [NSString stringWithFormat:@"id=%@,\ncard.id=%@,\ncard.last4=%@,\ncard.exp=%hhu/" 37 | @"%hu,\ncard.name=%@,\ncard.email=%@,\ncard.phone=%@", 38 | token.identifer, token.card.identifer, token.card.last4Number, 39 | token.card.expirationMonth, token.card.expirationYear, 40 | token.card.name, token.card.email, token.card.phone]; 41 | } 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /example-objc/example-objc/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // example-objc 4 | // 5 | // Created by Tatsuya Kitagawa on 2017/12/08. 6 | // Copyright © 2017年 PAY, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UITableViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /example-objc/example-objc/en.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Localizable.strings 3 | example-objc 4 | 5 | Created by Tadashi Wakayanagi on 2019/08/27. 6 | Copyright © 2019 PAY, Inc. All rights reserved. 7 | */ 8 | 9 | "example_card_information_section" = "Card Information"; 10 | "example_token_id_section" = "Token ID"; 11 | -------------------------------------------------------------------------------- /example-objc/example-objc/ja.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Localizable.strings 3 | example-objc 4 | 5 | Created by Tadashi Wakayanagi on 2019/08/27. 6 | Copyright © 2019 PAY, Inc. All rights reserved. 7 | */ 8 | 9 | "example_card_information_section" = "Card Information"; 10 | "example_token_id_section" = "Token ID"; 11 | -------------------------------------------------------------------------------- /example-objc/example-objc/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // example-objc 4 | // 5 | // Created by Tatsuya Kitagawa on 2017/12/08. 6 | // Copyright © 2017年 Tatsuya Kitagawa. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char* argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /example-swift/.gitignore: -------------------------------------------------------------------------------- 1 | Carthage/ 2 | Cartfile.resolved -------------------------------------------------------------------------------- /example-swift/Cartfile: -------------------------------------------------------------------------------- 1 | # github "payjp/payjp-ios" 2 | git "../" "HEAD" 3 | 4 | -------------------------------------------------------------------------------- /example-swift/example-swift-ui/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // example-swift-ui 4 | // 5 | // Created by Tatsuya Kitagawa on 2020/07/21. 6 | // 7 | 8 | import UIKit 9 | import PAYJP 10 | 11 | let PAYJPPublicKey = "pk_test_0383a1b8f91e8a6e3ea0e2a9" 12 | let App3DSRedirectURL = "exampleswift://tds/complete" 13 | let App3DSRedirectURLKey = "swift-app" 14 | 15 | @UIApplicationMain 16 | class AppDelegate: UIResponder, UIApplicationDelegate { 17 | 18 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 19 | // Override point for customization after application launch. 20 | PAYJPSDK.publicKey = PAYJPPublicKey 21 | PAYJPSDK.locale = Locale.current 22 | PAYJPSDK.threeDSecureURLConfiguration = 23 | ThreeDSecureURLConfiguration(redirectURL: URL(string: App3DSRedirectURL)!, 24 | redirectURLKey: App3DSRedirectURLKey) 25 | return true 26 | } 27 | 28 | func application(_ app: UIApplication, 29 | open url: URL, 30 | options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool { 31 | return ThreeDSecureProcessHandler.shared.completeThreeDSecureProcess(url: url) 32 | } 33 | 34 | // MARK: UISceneSession Lifecycle 35 | 36 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 37 | // Called when a new scene session is being created. 38 | // Use this method to select a configuration to create the new scene with. 39 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 40 | } 41 | 42 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { 43 | // Called when the user discards a scene session. 44 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 45 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /example-swift/example-swift-ui/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /example-swift/example-swift-ui/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /example-swift/example-swift-ui/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /example-swift/example-swift-ui/CardFormViewControllerExampleView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CardFormViewControllerExampleView.swift 3 | // example-swift 4 | // 5 | 6 | import SwiftUI 7 | import PAYJP 8 | 9 | struct CardFormViewControllerExampleView: View { 10 | @ObservedObject private var cardFormDelegate = CardFormDelegate() 11 | var body: some View { 12 | Button("Add Credit Card") { 13 | self.cardFormDelegate.isPresented.toggle() 14 | } 15 | .sheet(isPresented: self.$cardFormDelegate.isPresented) { 16 | CardFormViewControllerWrapper(delegate: self.cardFormDelegate) 17 | } 18 | } 19 | } 20 | 21 | // MARK: - CardFormViewControllerDelegate 22 | 23 | class CardFormDelegate: ObservableObject, CardFormViewControllerDelegate { 24 | @Published var isPresented: Bool = false 25 | 26 | func cardFormViewController(_: CardFormViewController, didCompleteWith result: CardFormResult) { 27 | switch result { 28 | case .cancel: 29 | print("CardFormResult.cancel") 30 | case .success: 31 | print("CardFormResult.success") 32 | DispatchQueue.main.async { [weak self] in 33 | self?.isPresented.toggle() 34 | } 35 | } 36 | } 37 | 38 | func cardFormViewController(_: CardFormViewController, 39 | didProduced token: Token, 40 | completionHandler: @escaping (Error?) -> Void) { 41 | print("token = \(String(describing: token.rawValue))") 42 | // TODO: send token to server 43 | completionHandler(nil) 44 | } 45 | } 46 | 47 | // MARK: - CardFormViewControllerWrapper 48 | 49 | struct CardFormViewControllerWrapper: UIViewControllerRepresentable { 50 | weak var delegate: CardFormViewControllerDelegate! 51 | 52 | func makeUIViewController(context: Context) -> UINavigationController { 53 | let cardFormVc = CardFormViewController.createCardFormViewController(delegate: delegate, 54 | viewType: .displayStyled) 55 | let naviVc = UINavigationController(rootViewController: cardFormVc) 56 | naviVc.presentationController?.delegate = cardFormVc 57 | return naviVc 58 | } 59 | 60 | func updateUIViewController(_ uiViewController: UINavigationController, context: Context) { 61 | } 62 | 63 | typealias UIViewControllerType = UINavigationController 64 | } 65 | -------------------------------------------------------------------------------- /example-swift/example-swift-ui/ContentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // example-swift-ui 4 | // 5 | // Created by Tatsuya Kitagawa on 2020/07/21. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ContentView: View { 11 | var body: some View { 12 | NavigationView { 13 | List { 14 | Section(header: Text("カードフォーム")) { 15 | NavigationLink(destination: CardFormViewControllerExampleView()) { 16 | Text("Card Form ViewController") 17 | } 18 | } 19 | Section(header: Text("支払い時の3Dセキュア、または顧客カードの3Dセキュア")) { 20 | NavigationLink(destination: ThreeDSecureProcessHandlerExampleView()) { 21 | Text("ThreeD Secure Process Handler") 22 | } 23 | } 24 | } 25 | .navigationBarTitle("Example") 26 | } 27 | } 28 | } 29 | 30 | 31 | // MARK: - Preview 32 | 33 | struct ContentView_Previews: PreviewProvider { 34 | static var previews: some View { 35 | ContentView() 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /example-swift/example-swift-ui/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UIApplicationSceneManifest 24 | 25 | UIApplicationSupportsMultipleScenes 26 | 27 | UISceneConfigurations 28 | 29 | UIWindowSceneSessionRoleApplication 30 | 31 | 32 | UISceneConfigurationName 33 | Default Configuration 34 | UISceneDelegateClassName 35 | $(PRODUCT_MODULE_NAME).SceneDelegate 36 | 37 | 38 | 39 | 40 | UILaunchStoryboardName 41 | LaunchScreen 42 | UIRequiredDeviceCapabilities 43 | 44 | armv7 45 | 46 | UISupportedInterfaceOrientations 47 | 48 | UIInterfaceOrientationPortrait 49 | UIInterfaceOrientationLandscapeLeft 50 | UIInterfaceOrientationLandscapeRight 51 | 52 | UISupportedInterfaceOrientations~ipad 53 | 54 | UIInterfaceOrientationPortrait 55 | UIInterfaceOrientationPortraitUpsideDown 56 | UIInterfaceOrientationLandscapeLeft 57 | UIInterfaceOrientationLandscapeRight 58 | 59 | CFBundleURLTypes 60 | 61 | 62 | CFBundleURLName 63 | $(PRODUCT_BUNDLE_IDENTIFIER) 64 | CFBundleURLSchemes 65 | 66 | exampleswift 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /example-swift/example-swift-ui/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /example-swift/example-swift.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example-swift/example-swift.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example-swift/example-swift/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // example-swift 4 | // 5 | // Created by Tatsuya Kitagawa on 2017/12/08. 6 | // Copyright © 2017年 PAY, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import PAYJP 11 | 12 | let PAYJPPublicKey = "pk_test_0383a1b8f91e8a6e3ea0e2a9" 13 | let App3DSRedirectURL = "exampleswift://tds/complete" 14 | let App3DSRedirectURLKey = "swift-app" 15 | 16 | @UIApplicationMain 17 | class AppDelegate: UIResponder, UIApplicationDelegate { 18 | 19 | var window: UIWindow? 20 | 21 | func application( 22 | _ application: UIApplication, 23 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil 24 | ) -> Bool { 25 | 26 | PAYJPSDK.publicKey = PAYJPPublicKey 27 | PAYJPSDK.locale = Locale.current 28 | PAYJPSDK.threeDSecureURLConfiguration = 29 | ThreeDSecureURLConfiguration(redirectURL: URL(string: App3DSRedirectURL)!, 30 | redirectURLKey: App3DSRedirectURLKey) 31 | 32 | return true 33 | } 34 | 35 | func application(_ app: UIApplication, 36 | open url: URL, 37 | options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool { 38 | 39 | return ThreeDSecureProcessHandler.shared.completeThreeDSecureProcess(url: url) 40 | } 41 | 42 | func applicationWillResignActive(_ application: UIApplication) { 43 | } 44 | 45 | func applicationDidEnterBackground(_ application: UIApplication) { 46 | } 47 | 48 | func applicationWillEnterForeground(_ application: UIApplication) { 49 | } 50 | 51 | func applicationDidBecomeActive(_ application: UIApplication) { 52 | } 53 | 54 | func applicationWillTerminate(_ application: UIApplication) { 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /example-swift/example-swift/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /example-swift/example-swift/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /example-swift/example-swift/ColorTheme.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ColorTheme.swift 3 | // example-swift 4 | // 5 | // Created by Tadashi Wakayanagi on 2019/10/15. 6 | // 7 | 8 | import UIKit 9 | 10 | enum ColorTheme: String { 11 | case Normal 12 | case Red 13 | case Blue 14 | case Dark 15 | } 16 | 17 | extension UIColor { 18 | 19 | convenience init(_ red: Int, _ green: Int, _ blue: Int, _ alpha: Int = 255) { 20 | let rgba = [red, green, blue, alpha].map { i -> CGFloat in 21 | switch i { 22 | case let i where i < 0: 23 | return 0 24 | case let i where i > 255: 25 | return 1 26 | default: 27 | return CGFloat(i) / 255 28 | } 29 | } 30 | self.init(red: rgba[0], green: rgba[1], blue: rgba[2], alpha: rgba[3]) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /example-swift/example-swift/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 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 | CFBundleURLTypes 28 | 29 | 30 | CFBundleURLName 31 | $(PRODUCT_BUNDLE_IDENTIFIER) 32 | CFBundleURLSchemes 33 | 34 | exampleswift 35 | 36 | 37 | 38 | UIRequiredDeviceCapabilities 39 | 40 | armv7 41 | 42 | UISupportedInterfaceOrientations 43 | 44 | UIInterfaceOrientationPortrait 45 | UIInterfaceOrientationLandscapeLeft 46 | UIInterfaceOrientationLandscapeRight 47 | 48 | UISupportedInterfaceOrientations~ipad 49 | 50 | UIInterfaceOrientationPortrait 51 | UIInterfaceOrientationPortraitUpsideDown 52 | UIInterfaceOrientationLandscapeLeft 53 | UIInterfaceOrientationLandscapeRight 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /example-swift/example-swift/SampleService.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SampleService.swift 3 | // example-swift 4 | // 5 | // Created by Tadashi Wakayanagi on 2019/11/26. 6 | // 7 | 8 | import Foundation 9 | 10 | // TODO: REPLACE WITH YOUR ENDPOINT URL 11 | private let BACKEND_URL = "" 12 | private let API_PATH = "/save_card" 13 | 14 | enum SampleError: LocalizedError { 15 | case unexpected 16 | 17 | var errorDescription: String? { 18 | switch self { 19 | case .unexpected: 20 | return "予期しない問題が発生しました" 21 | } 22 | } 23 | } 24 | 25 | struct SampleService { 26 | 27 | static let shared = SampleService() 28 | 29 | func saveCard(withToken token: String, completion: @escaping (Error?) -> Void) { 30 | if BACKEND_URL.isEmpty { 31 | completion(nil) 32 | return 33 | } 34 | 35 | do { 36 | let dict = ["card": token] 37 | let data = try JSONSerialization.data(withJSONObject: dict, options: .prettyPrinted) 38 | let urlString = BACKEND_URL + API_PATH 39 | let request = NSMutableURLRequest(url: URL(string: urlString)!) 40 | request.cachePolicy = .reloadIgnoringLocalCacheData 41 | request.setValue("Application/json", forHTTPHeaderField: "Content-Type") 42 | request.httpMethod = "POST" 43 | request.httpBody = data 44 | 45 | let configuration = URLSessionConfiguration.ephemeral 46 | let session = URLSession(configuration: configuration) 47 | let dataTask = session.dataTask(with: request as URLRequest) { (_, response, error) in 48 | if let httpResponse = response as? HTTPURLResponse { 49 | print("Status code: \(httpResponse.statusCode)") 50 | 51 | if httpResponse.statusCode == 201 { 52 | print("SampleService Success.") 53 | completion(nil) 54 | } else { 55 | if let error = error { 56 | print("SampleService Error => \(error)") 57 | completion(error) 58 | } else { 59 | print("SampleService Error other.") 60 | completion(SampleError.unexpected) 61 | } 62 | } 63 | } 64 | } 65 | dataTask.resume() 66 | } catch { 67 | print("Error: \(error)") 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /example-swift/example-swift/ThreeDSecureExampleViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ThreeDSecureExampleViewController.swift 3 | // example-swift 4 | // 5 | 6 | import UIKit 7 | import PAYJP 8 | 9 | class ThreeDSecureExampleViewController: UIViewController { 10 | @IBOutlet private var startButton: UIButton! 11 | 12 | @IBOutlet private var textField: UITextField! 13 | 14 | @IBOutlet private var resultLabel: UILabel! 15 | 16 | private var pendingResourceId: String? 17 | 18 | override func viewDidLoad() { 19 | super.viewDidLoad() 20 | } 21 | 22 | @IBAction private func startThreeDSecure(_ sender: Any) { 23 | guard let resourceId = textField.text, !resourceId.isEmpty else { 24 | self.resultLabel.text = "" 25 | self.resultLabel.isHidden = true 26 | return 27 | } 28 | 29 | pendingResourceId = resourceId 30 | ThreeDSecureProcessHandler.shared.startThreeDSecureProcess(viewController: self, delegate: self, resourceId: resourceId) 31 | } 32 | } 33 | 34 | // MARK: - ThreeDSecureProcessHandlerDelegate 35 | 36 | extension ThreeDSecureExampleViewController: ThreeDSecureProcessHandlerDelegate { 37 | func threeDSecureProcessHandlerDidFinish(_ handler: ThreeDSecureProcessHandler, status: ThreeDSecureProcessStatus) { 38 | switch status { 39 | case .completed: 40 | DispatchQueue.main.async { 41 | self.resultLabel.text = "3Dセキュア認証が終了しました。\nこの結果をサーバーサイドに伝え、完了処理や結果のハンドリングを行なってください。\n後続処理の実装方法に関してはドキュメントをご参照ください。" 42 | self.resultLabel.textColor = .black 43 | self.resultLabel.isHidden = false 44 | } 45 | case .canceled: 46 | DispatchQueue.main.async { 47 | self.resultLabel.text = "3Dセキュア認証がキャンセルされました。" 48 | self.resultLabel.textColor = .red 49 | self.resultLabel.isHidden = false 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /example-swift/example-swift/en.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Localizable.strings 3 | example-swift 4 | 5 | Created by Tadashi Wakayanagi on 2019/09/12. 6 | 7 | */ 8 | 9 | "example_card_information_section" = "Card Information"; 10 | "example_token_id_section" = "Token ID"; 11 | -------------------------------------------------------------------------------- /example-swift/example-swift/ja.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Localizable.strings 3 | example-swift 4 | 5 | Created by Tadashi Wakayanagi on 2019/09/12. 6 | 7 | */ 8 | 9 | "example_card_information_section" = "Card Information"; 10 | "example_token_id_section" = "Token ID"; 11 | -------------------------------------------------------------------------------- /example-swift/run-swiftlint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -ex 2 | if which swiftlint >/dev/null; then 3 | swiftlint lint path example-swift/*.swift --config ../.swiftlint.yml 4 | else 5 | echo "warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint" 6 | fi 7 | 8 | -------------------------------------------------------------------------------- /fastlane/Pluginfile: -------------------------------------------------------------------------------- 1 | # Autogenerated by fastlane 2 | # 3 | # Ensure this file is checked in to source control! 4 | 5 | gem 'fastlane-plugin-firebase_app_distribution' 6 | -------------------------------------------------------------------------------- /podspec.rb: -------------------------------------------------------------------------------- 1 | module PAYJPSDK 2 | VERSION = '2.2.1' 3 | HOMEPAGE_URL = 'https://github.com/payjp/payjp-ios' 4 | LICENSE = { :type => 'MIT' } 5 | AUTHOR = { 'PAY.JP (https://pay.jp)' => 'support@pay.jp' } 6 | SOURCE = { :git => 'https://github.com/payjp/payjp-ios.git', :tag => VERSION } 7 | MODULE_NAME = 'PAYJP' 8 | SWIFT_VERSIONS = ['5.0', '5.1', '5.2', '5.3', '5.4', '5.5'] 9 | IOS_DEPLOYMENT_TARGET = '12.0' 10 | SOURCE_FILES = ['Sources/**/*.{h,m,swift}'] 11 | RESOURCE_BUNDLES = { 'PAYJP' => ['Sources/Resources/**/*'] } 12 | RESOURCES = [ 'Sources/Resources/Assets.xcassets' ] 13 | PUBLIC_HEADER_FILES = 'Sources/**/*.h' 14 | FRAMEWORKS = 'PassKit' 15 | POD_TARGET_XCCONFIG = { 'OTHER_SWIFT_FLAGS' => '-DPAYJPSDKCocoaPods' } 16 | end 17 | -------------------------------------------------------------------------------- /scripts/bash.source: -------------------------------------------------------------------------------- 1 | if [[ -z "$REPOSITORY_ROOT" ]]; then 2 | export REPOSITORY_ROOT="$(git rev-parse --show-toplevel)" 3 | fi 4 | 5 | export ENTERPRISE_P12_FILE=$REPOSITORY_ROOT/BASE_Enterprise.p12 6 | export PROFILE_EXAMPLE_OBJC_APP_FILE=$REPOSITORY_ROOT/payjpiosexampleobjc.mobileprovision 7 | -------------------------------------------------------------------------------- /scripts/carthage.sh: -------------------------------------------------------------------------------- 1 | # carthage.sh 2 | # Usage example: ./carthage.sh build --platform iOS 3 | 4 | set -euo pipefail 5 | 6 | xcconfig=$(mktemp /tmp/static.xcconfig.XXXXXX) 7 | trap 'rm -f "$xcconfig"' INT TERM HUP EXIT 8 | 9 | echo "IPHONEOS_DEPLOYMENT_TARGET = 12.0" >> $xcconfig 10 | 11 | export XCODE_XCCONFIG_FILE="$xcconfig" 12 | carthage "$@" -------------------------------------------------------------------------------- /scripts/restore_signing_credential.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -ex 2 | echo -n $ENTERPRISE_P12_ENC | base64 --decode > $ENTERPRISE_P12_FILE 3 | echo -n $PROFILE_EXAMPLE_OBJC_APP_ENC | base64 --decode > $PROFILE_EXAMPLE_OBJC_APP_FILE 4 | -------------------------------------------------------------------------------- /scripts/run-clang-format.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -ex 2 | ROOT="$(git rev-parse --show-toplevel)" 3 | find $ROOT/Sources/ -iname *.h -o -iname *.m | xargs clang-format -i -style=Google 4 | find $ROOT/example-objc/example-objc/ -iname *.h -o -iname *.m | xargs clang-format -i -style=Google -------------------------------------------------------------------------------- /scripts/run-swift-format.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -ex 2 | ROOT="$(git rev-parse --show-toplevel)" 3 | swiftlint --fix --format $ROOT --config $ROOT/.swiftlint.yml 4 | swiftlint --fix --format $ROOT/example-swift/**/*.swift --config $ROOT/.swiftlint.yml 5 | --------------------------------------------------------------------------------