diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index fb98c79..f4060b7 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -5,13 +5,17 @@ runs: using: composite steps: - name: Setup Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version-file: .nvmrc + - name: Enable Corepack + run: corepack enable + shell: bash + - name: Cache dependencies id: yarn-cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | **/node_modules diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0c7ff3c..9b7013e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup uses: ./.github/actions/setup @@ -30,7 +30,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup uses: ./.github/actions/setup @@ -42,7 +42,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup uses: ./.github/actions/setup @@ -56,13 +56,13 @@ jobs: TURBO_CACHE_DIR: .turbo/android steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup uses: ./.github/actions/setup - name: Cache turborepo for Android - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ${{ env.TURBO_CACHE_DIR }} key: ${{ runner.os }}-turborepo-android-${{ hashFiles('yarn.lock') }} @@ -79,7 +79,7 @@ jobs: - name: Install JDK if: env.turbo_cache_hit != 1 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: '17' @@ -91,7 +91,7 @@ jobs: - name: Cache Gradle if: env.turbo_cache_hit != 1 - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | ~/.gradle/wrapper @@ -102,23 +102,26 @@ jobs: - name: Build example for Android env: - JAVA_OPTS: "-XX:MaxHeapSize=6g" + JAVA_OPTS: '-XX:MaxHeapSize=6g' run: | yarn turbo run build:android --cache-dir="${{ env.TURBO_CACHE_DIR }}" build-ios: - runs-on: macos-14 + runs-on: macos-15 env: TURBO_CACHE_DIR: .turbo/ios steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup uses: ./.github/actions/setup + - name: Install xcbeautify + run: brew install xcbeautify + - name: Cache turborepo for iOS - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ${{ env.TURBO_CACHE_DIR }} key: ${{ runner.os }}-turborepo-ios-${{ hashFiles('yarn.lock') }} @@ -133,19 +136,21 @@ jobs: echo "turbo_cache_hit=1" >> $GITHUB_ENV fi - - name: Cache cocoapods + - name: Cache CocoaPods if: env.turbo_cache_hit != 1 id: cocoapods-cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: - path: | - **/ios/Pods + path: example/ios/Pods key: ${{ runner.os }}-cocoapods-${{ hashFiles('example/ios/Podfile.lock') }} restore-keys: | ${{ runner.os }}-cocoapods- - - name: Install cocoapods - if: env.turbo_cache_hit != 1 && steps.cocoapods-cache.outputs.cache-hit != 'true' + # React Native 0.76+ requires pod install to run even when Pods are cached, + # because it generates ReactAppDependencyProvider.podspec in build/generated/ios/. + # Skipping pod install on cache hit causes "No podspec found" errors. + - name: Install CocoaPods + if: env.turbo_cache_hit != 1 run: | cd example/ios pod install @@ -153,5 +158,17 @@ jobs: NO_FLIPPER: 1 - name: Build example for iOS + if: env.turbo_cache_hit != 1 run: | - yarn turbo run build:ios --cache-dir="${{ env.TURBO_CACHE_DIR }}" + cd example/ios + xcodebuild \ + -workspace MsdkReactNativeExample.xcworkspace \ + -scheme MsdkReactNativeExample \ + -configuration Debug \ + -sdk iphonesimulator \ + -destination "platform=iOS Simulator,name=iPhone 16,OS=latest" \ + -derivedDataPath ~/DerivedData \ + COMPILER_INDEX_STORE_ENABLE=NO \ + CLANG_ENABLE_MODULE_DEBUGGING=NO \ + build > xcodebuild.log 2>&1 || (cat xcodebuild.log | xcbeautify && exit 1) + cat xcodebuild.log | xcbeautify diff --git a/.gitignore b/.gitignore index d8b1e7a..c491960 100644 --- a/.gitignore +++ b/.gitignore @@ -50,6 +50,7 @@ example/vendor/ # node.js # node_modules/ +example/node_modules/ npm-debug.log yarn-debug.log yarn-error.log diff --git a/.nvmrc b/.nvmrc index 3f430af..9a2a0e2 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v18 +v20 diff --git a/.yarnrc.yml b/.yarnrc.yml new file mode 100644 index 0000000..3186f3f --- /dev/null +++ b/.yarnrc.yml @@ -0,0 +1 @@ +nodeLinker: node-modules diff --git a/CHANGELOG.md b/CHANGELOG.md index aa431ff..44e832e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,3 +2,4 @@ ## [0.2.1] Update internal SDK libraries to android 3.8.8 and iOS 3.8.5 ## [0.2.2] Update internal SDK libraries to android 3.8.9 and iOS 3.8.8. Upgrade React Native version ## [0.3.0] Update internal SDK libraries to android 3.9.1 and iOS 4.0.0. Change ios integration to SPM +## [0.3.1] Update internal SDK libraries to android 3.10.2 and iOS 4.0.7 diff --git a/android/build.gradle b/android/build.gradle index 6aed97e..9e9d716 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -96,7 +96,7 @@ def kotlin_version = getExtOrDefault("kotlinVersion") dependencies { coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.3") - implementation "com.ecommpay:msdk-ui:3.9.1" + implementation "com.ecommpay:msdk-ui:3.10.2" implementation "com.facebook.react:react-native:+" implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" diff --git a/example/Gemfile b/example/Gemfile index 2a7ce35..e69022a 100644 --- a/example/Gemfile +++ b/example/Gemfile @@ -6,3 +6,4 @@ ruby ">= 2.6.10" # Exclude problematic versions of cocoapods and activesupport that causes build failures. gem 'cocoapods', '>= 1.13', '!= 1.15.0', '!= 1.15.1' gem 'activesupport', '>= 6.1.7.5', '!= 7.1.0' +gem 'nkf' diff --git a/example/ios/.xcode.env.local b/example/ios/.xcode.env.local index 34d2a70..b612a49 100644 --- a/example/ios/.xcode.env.local +++ b/example/ios/.xcode.env.local @@ -1 +1 @@ -export NODE_BINARY=/opt/homebrew/bin/node +export NODE_BINARY=/Users/satiryc/.nvm/versions/node/v20.20.2/bin/node diff --git a/example/ios/MsdkReactNativeExample.xcworkspace/xcshareddata/swiftpm/Package.resolved b/example/ios/MsdkReactNativeExample.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000..043fde2 --- /dev/null +++ b/example/ios/MsdkReactNativeExample.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,15 @@ +{ + "originHash" : "1f8a50bb7ada8d01703987854dd44acf406aa27c9323f1096103f8eeeb2b51a9", + "pins" : [ + { + "identity" : "mobile-sdk-ios-ui", + "kind" : "remoteSourceControl", + "location" : "https://github.com/ITECOMMPAY/mobile-sdk-ios-ui.git", + "state" : { + "revision" : "0473d5d021ed2c126867ca47f9f65a9d9f956558", + "version" : "4.0.7" + } + } + ], + "version" : 3 +} diff --git a/example/ios/MsdkReactNativeExample/Info.plist b/example/ios/MsdkReactNativeExample/Info.plist index b771a2b..2e27ad6 100644 --- a/example/ios/MsdkReactNativeExample/Info.plist +++ b/example/ios/MsdkReactNativeExample/Info.plist @@ -34,7 +34,7 @@ NSLocationWhenInUseUsageDescription RCTNewArchEnabled - + UILaunchStoryboardName LaunchScreen UIRequiredDeviceCapabilities diff --git a/example/ios/Podfile b/example/ios/Podfile index 31d6379..ec0ffea 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -7,7 +7,7 @@ require Pod::Executable.execute_command('node', ['-p', platform :ios, 16.6 prepare_react_native_project! -ENV['RCT_NEW_ARCH_ENABLED'] = '1' +ENV['RCT_NEW_ARCH_ENABLED'] = '0' linkage = ENV['USE_FRAMEWORKS'] if linkage != nil @@ -25,12 +25,22 @@ target 'MsdkReactNativeExample' do ) post_install do |installer| - # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202 react_native_post_install( installer, config[:reactNativePath], :mac_catalyst_enabled => false, - # :ccache_enabled => true ) + + installer.pods_project.targets.each do |target| + target.build_configurations.each do |config| + if target.name == 'fmt' + config.build_settings['CLANG_CXX_LANGUAGE_STANDARD'] = 'c++17' + else + config.build_settings['CLANG_CXX_LANGUAGE_STANDARD'] = 'c++20' + end + + config.build_settings['CLANG_ENABLE_MODULE_DEBUGGING'] = 'NO' + end + end end end diff --git a/example/node_modules b/example/node_modules deleted file mode 100644 index 68a084a..0000000 --- a/example/node_modules +++ /dev/null @@ -1 +0,0 @@ -../node_modules \ No newline at end of file diff --git a/example/package.json b/example/package.json index 7078ded..cef3b55 100644 --- a/example/package.json +++ b/example/package.json @@ -10,9 +10,9 @@ "build:ios": "react-native build-ios --scheme MsdkReactNativeExample --mode Debug --extra-params \"-sdk iphonesimulator CC=clang CPLUSPLUS=clang++ LD=clang LDPLUSPLUS=clang++ GCC_OPTIMIZATION_LEVEL=0 GCC_PRECOMPILE_PREFIX_HEADER=YES ASSETCATALOG_COMPILER_OPTIMIZATION=time DEBUG_INFORMATION_FORMAT=dwarf COMPILER_INDEX_STORE_ENABLE=NO\"" }, "dependencies": { - "react": "19.1.0", - "react-native": "^0.81.3", - "msdk-react-native": "*" + "msdk-react-native": "*", + "react": "19.1.4", + "react-native": "^0.81.3" }, "devDependencies": { "@babel/core": "^7.20.0", diff --git a/example/src/App.tsx b/example/src/App.tsx index 7b7df25..9324179 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useState, type JSX } from 'react'; import { StyleSheet, View, @@ -7,7 +7,7 @@ import { Button, ScrollView, Switch, - useColorScheme + useColorScheme, } from 'react-native'; import { initializePayment, @@ -18,21 +18,24 @@ import { type EcmpAdditionalField, type EcmpRecipientInfo, type EcmpRecurrentData, - getParamsForSignature + getParamsForSignature, } from 'msdk-react-native'; import SignatureGenerator from '../../src/index'; -export default function App() { +const App = (): JSX.Element => { const colorScheme = useColorScheme(); const isDark = colorScheme === 'dark'; const [paymentResult, setPaymentResult] = useState(''); // Payment Info State const [projectID, setProjectID] = useState('138723'); - const [paymentID, setPaymentID] = useState('sdk_sample_ui_' + Math.random().toString(36).substring(2, 9)); + const [paymentID, setPaymentID] = useState( + 'sdk_sample_ui_' + Math.random().toString(36).substring(2, 9) + ); const [paymentAmount, setPaymentAmount] = useState('100.50'); const [paymentCurrency, setPaymentCurrency] = useState('USD'); - const [paymentDescription, setPaymentDescription] = useState('Test payment'); + const [paymentDescription, setPaymentDescription] = + useState('Test payment'); const [customerID, setCustomerID] = useState('12'); const [regionCode, setRegionCode] = useState('US'); const [token, setToken] = useState(''); @@ -41,23 +44,31 @@ export default function App() { const [secret, setSecret] = useState('secret key'); // Payment Options State - const [actionType, setActionType] = useState(EcmpActionType.Sale); + // const [actionType, setActionType] = useState( + // EcmpActionType.Sale + // ); const [isDarkTheme, setIsDarkTheme] = useState(false); - const [mockModeType, setMockModeType] = useState(EcmpMockModeType.disabled); + const [mockModeType, setMockModeType] = useState( + EcmpMockModeType.disabled + ); const [hideScanningCards, setHideScanningCards] = useState(true); const [hideSavedWallets, setHideSavedWallets] = useState(false); const [primaryBrandColor, setPrimaryBrandColor] = useState('#3498db'); - const [secondaryBrandColor, setSecondaryBrandColor] = useState('#CAB2FF'); + const [secondaryBrandColor, setSecondaryBrandColor] = + useState('#CAB2FF'); const [storedCardType, setStoredCardType] = useState(''); // Screen Display Modes - const [hideSuccessFinalPage, setHideSuccessFinalPage] = useState(false); - const [hideDeclineFinalPage, setHideDeclineFinalPage] = useState(false); + const [hideSuccessFinalPage, setHideSuccessFinalPage] = + useState(false); + const [hideDeclineFinalPage, setHideDeclineFinalPage] = + useState(false); // Google Pay const [googleMerchantId, setGoogleMerchantId] = useState(''); const [googleMerchantName, setGoogleMerchantName] = useState(''); - const [googleIsTestEnvironment, setGoogleIsTestEnvironment] = useState(true); + const [googleIsTestEnvironment, setGoogleIsTestEnvironment] = + useState(true); // Apple Pay const [applePayMerchantID, setApplePayMerchantID] = useState(''); @@ -67,7 +78,9 @@ export default function App() { // Additional Fields const [additionalFieldType, setAdditionalFieldType] = useState(''); const [additionalFieldValue, setAdditionalFieldValue] = useState(''); - const [additionalFields, setAdditionalFields] = useState([]); + const [additionalFields, setAdditionalFields] = useState< + EcmpAdditionalField[] + >([]); // Recipient Info const [showRecipientInfo, setShowRecipientInfo] = useState(false); @@ -95,10 +108,13 @@ export default function App() { const addAdditionalField = () => { if (additionalFieldType && additionalFieldValue) { - setAdditionalFields([...additionalFields, { - type: additionalFieldType, - value: additionalFieldValue - }]); + setAdditionalFields([ + ...additionalFields, + { + type: additionalFieldType, + value: additionalFieldValue, + }, + ]); setAdditionalFieldType(''); setAdditionalFieldValue(''); } @@ -111,7 +127,7 @@ export default function App() { const handlePayment = async (paymentActionType: EcmpActionType) => { try { const paymentInfoWithoutSignature: EcmpPaymentInfo = { - projectID: parseInt(projectID) || 0, + projectID: parseInt(projectID, 10) || 0, paymentID: paymentID, paymentAmount: parseFloat(paymentAmount) || 0, paymentCurrency: paymentCurrency, @@ -124,13 +140,17 @@ export default function App() { hideSavedWallets: hideSavedWallets, }; - const paramsForSignature = await getParamsForSignature(paymentInfoWithoutSignature); - + const paramsForSignature = await getParamsForSignature( + paymentInfoWithoutSignature + ); - const signature = SignatureGenerator.generateSignature(paramsForSignature, secret); + const signature = SignatureGenerator.generateSignature( + paramsForSignature, + secret + ); const paymentInfo = { - projectID: parseInt(projectID) || 0, + projectID: parseInt(projectID, 10) || 0, paymentID: paymentID, paymentAmount: parseFloat(paymentAmount) || 0, paymentCurrency: paymentCurrency, @@ -141,36 +161,42 @@ export default function App() { languageCode: languageCode || undefined, receiptData: receiptData || undefined, hideSavedWallets: hideSavedWallets, - signature: signature - } + signature: signature, + }; const screenDisplayModes = []; - if (hideSuccessFinalPage) screenDisplayModes.push(EcmpScreenDisplayMode.hideSuccessFinalPage); - if (hideDeclineFinalPage) screenDisplayModes.push(EcmpScreenDisplayMode.hideDeclineFinalPage); - - const recipientInfo: EcmpRecipientInfo | undefined = showRecipientInfo ? { - walletOwner: walletOwner || undefined, - walletId: walletId || undefined, - country: recipientCountry || undefined, - pan: pan || undefined, - cardHolder: cardHolder || undefined, - address: address || undefined, - city: city || undefined, - stateCode: stateCode || undefined - } : undefined; - - const recurrentData: EcmpRecurrentData | undefined = showRecurrentData ? { - register: recurrentRegister, - type: recurrentType || undefined, - expiryDay: expiryDay || undefined, - expiryMonth: expiryMonth || undefined, - expiryYear: expiryYear || undefined, - period: period || undefined, - interval: parseInt(interval) || undefined, - time: time || undefined, - startDate: startDate || undefined, - scheduledPaymentID: scheduledPaymentID || undefined - } : undefined; + if (hideSuccessFinalPage) + screenDisplayModes.push(EcmpScreenDisplayMode.hideSuccessFinalPage); + if (hideDeclineFinalPage) + screenDisplayModes.push(EcmpScreenDisplayMode.hideDeclineFinalPage); + + const recipientInfo: EcmpRecipientInfo | undefined = showRecipientInfo + ? { + walletOwner: walletOwner || undefined, + walletId: walletId || undefined, + country: recipientCountry || undefined, + pan: pan || undefined, + cardHolder: cardHolder || undefined, + address: address || undefined, + city: city || undefined, + stateCode: stateCode || undefined, + } + : undefined; + + const recurrentData: EcmpRecurrentData | undefined = showRecurrentData + ? { + register: recurrentRegister, + type: recurrentType || undefined, + expiryDay: expiryDay || undefined, + expiryMonth: expiryMonth || undefined, + expiryYear: expiryYear || undefined, + period: period || undefined, + interval: parseInt(interval, 10) || undefined, + time: time || undefined, + startDate: startDate || undefined, + scheduledPaymentID: scheduledPaymentID || undefined, + } + : undefined; await initializePayment( { @@ -178,8 +204,10 @@ export default function App() { paymentInfo, isDarkTheme, mockModeType, - screenDisplayModes: screenDisplayModes.length > 0 ? screenDisplayModes : undefined, - additionalFields: additionalFields.length > 0 ? additionalFields : undefined, + screenDisplayModes: + screenDisplayModes.length > 0 ? screenDisplayModes : undefined, + additionalFields: + additionalFields.length > 0 ? additionalFields : undefined, recipientInfo, recurrentData, hideScanningCards, @@ -192,7 +220,7 @@ export default function App() { primaryBrandColor: primaryBrandColor, secondaryBrandColor: secondaryBrandColor, hideFooterLogo: true, - storedCardType: parseInt(storedCardType) || undefined + storedCardType: parseInt(storedCardType, 10) || undefined, }, (result) => { console.log('Payment result:', result); @@ -217,6 +245,7 @@ export default function App() { @@ -335,22 +364,34 @@ export default function App() { Hide Saved Wallets - + Hide Scanning Cards - + Hide Success Final Page - + Hide Decline Final Page - + @@ -361,17 +402,23 @@ export default function App() {