Skip to content

Commit

Permalink
upgrade to mobilepaymentsdk to 2.1.0 and add tap to pay methods for i…
Browse files Browse the repository at this point in the history
…os (#39)

* upgrade to 2.1.0 and add tap to pay methods

* fix Freeland and Plinio comments

* remove console.log
  • Loading branch information
cesar-sosa-hol authored Feb 28, 2025
1 parent 476d85c commit 04f3550
Show file tree
Hide file tree
Showing 12 changed files with 178 additions and 26 deletions.
16 changes: 8 additions & 8 deletions example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ PODS:
- hermes-engine (0.75.3):
- hermes-engine/Pre-built (= 0.75.3)
- hermes-engine/Pre-built (0.75.3)
- mobile-payments-sdk-react-native (2025.2.0):
- mobile-payments-sdk-react-native (2025.2.1):
- DoubleConversion
- glog
- hermes-engine
- MockReaderUI (~> 2.0.2)
- MockReaderUI (~> 2.1.0)
- RCT-Folly (= 2024.01.01.00)
- RCTRequired
- RCTTypeSafety
Expand All @@ -28,9 +28,9 @@ PODS:
- ReactCodegen
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- SquareMobilePaymentsSDK (~> 2.0.2)
- SquareMobilePaymentsSDK (~> 2.1.0)
- Yoga
- MockReaderUI (2.0.2)
- MockReaderUI (2.1.0)
- Permission-BluetoothPeripheral (3.10.1):
- RNPermissions
- Permission-LocationAccuracy (3.10.1):
Expand Down Expand Up @@ -1579,7 +1579,7 @@ PODS:
- ReactCommon/turbomodule/core
- Yoga
- SocketRocket (0.7.0)
- SquareMobilePaymentsSDK (2.0.2)
- SquareMobilePaymentsSDK (2.1.0)
- Yoga (0.0.0)

DEPENDENCIES:
Expand Down Expand Up @@ -1818,8 +1818,8 @@ SPEC CHECKSUMS:
fmt: 4c2741a687cc09f0634a2e2c72a838b99f1ff120
glog: 69ef571f3de08433d766d614c73a9838a06bf7eb
hermes-engine: 8d2103d6c0176779aea4e25df6bb1410f9946680
mobile-payments-sdk-react-native: f8b78eb939714c7368b6268a8f3591b7785d6219
MockReaderUI: 859bff6aaab222b59c1d8839efad2d7fca22135a
mobile-payments-sdk-react-native: 267ce60e2818bf09c363e2d201e3c506d94a250b
MockReaderUI: 141fe6bb56f63d22e375800ee6a5c5da69ffb55a
Permission-BluetoothPeripheral: 34ab829f159c6cf400c57bac05f5ba1b0af7a86e
Permission-LocationAccuracy: 30c5421911024b28d8916db5cbd728097da54434
Permission-LocationAlways: af165dee8a5a5888df6764f9f6ba98b112893709
Expand Down Expand Up @@ -1885,7 +1885,7 @@ SPEC CHECKSUMS:
RNScreens: 35bb8e81aeccf111baa0ea01a54231390dbbcfd9
RNVectorIcons: 182892e7d1a2f27b52d3c627eca5d2665a22ee28
SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d
SquareMobilePaymentsSDK: 1964169130cab2f447d767adf9c18f8b5d212ff6
SquareMobilePaymentsSDK: b437afc89530142af80ed818f4db02e07c47457c
Yoga: 4ef80d96a5534f0e01b3055f17d1e19a9fc61b63

PODFILE CHECKSUM: 439764333dadb7461aea4b653e55d92f832f43a0
Expand Down
4 changes: 2 additions & 2 deletions example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ export default function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
<Stack.Screen
name="Home"
component={HomeView}
options={{ headerShown: false }}
/>
<Stack.Screen
<Stack.Screen
name="Permissions"
component={PermissionsScreen}
options={{ headerShown: false }}
Expand Down
1 change: 1 addition & 0 deletions example/src/Screens/HomeScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ const HomeView = () => {
const presentMockReader = async () => {
try {
const result = await showMockReaderUI();
console.log(result);
setMockReaderPresented(true);
} catch (error) {
console.log('Mock Reader UI error:', error);
Expand Down
14 changes: 7 additions & 7 deletions example/src/Screens/PermissionsScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,7 @@ const PermissionsView = () => {
useState(false);
const [bluetoothPermissionGranted, setBluetoothPermissionGranted] =
useState(false);
const [readPhoneStateGranted, setReadPhoneStateGranted] =
useState(isIos);
const [readPhoneStateGranted, setReadPhoneStateGranted] = useState(isIos);
const [isLoading, setIsLoading] = useState(false);
const [isAuthorized, setIsAuthorized] = useState(false);
const navigation = useNavigation();
Expand All @@ -158,6 +157,7 @@ const PermissionsView = () => {
'MOBILE_PAYMENT_SDK_ACCESS_TOKEN',
'MOBILE_PAYMENT_SDK_LOCATION_ID'
);
console.log(auth);
let authorizedLocation = await getAuthorizedLocation();
let authorizationState = await getAuthorizationState();
setIsAuthorized(true);
Expand All @@ -169,9 +169,7 @@ const PermissionsView = () => {
);
} catch (error) {
setIsAuthorized(false);
console.log(
'Authorization error: ', JSON.stringify(error)
);
console.log('Authorization error: ', JSON.stringify(error));
Alert.alert('Error Authenticating', error.message);
}
setIsLoading(false);
Expand Down Expand Up @@ -206,7 +204,7 @@ const PermissionsView = () => {
// Remember to remove your observer once the component has been removed from the DOM
stopObservingAuthorizationChanges();
};
});
}, []);

return (
<SafeAreaView style={styles.container}>
Expand Down Expand Up @@ -248,7 +246,9 @@ const PermissionsView = () => {
activeLabel="Sign in"
inactiveLabel="Sign out"
/>
<Text style={isAuthorized ? styles.statusText : styles.statusTextInactive}>
<Text
style={isAuthorized ? styles.statusText : styles.statusTextInactive}
>
{isAuthorized ? 'This device is Authorized' : 'Device not Authorized'}
</Text>
</ScrollView>
Expand Down
8 changes: 7 additions & 1 deletion example/src/components/LoadingButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,13 @@ const LoadingButton = ({
{isLoading ? (
<ActivityIndicator color="#000" />
) : (
<Text style={isActive ? styles.loadingButtonText : styles.loadingButtonTextInactive}>
<Text
style={
isActive
? styles.loadingButtonText
: styles.loadingButtonTextInactive
}
>
{isActive ? activeLabel : inactiveLabel}
</Text>
)}
Expand Down
7 changes: 5 additions & 2 deletions example/src/components/PermissionRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ const PermissionRow = ({ title, description, isGranted, onRequest }) => (
size={25}
fillColor="gray"
unFillColor="#FFFFFF"
innerIconStyle={{ borderWidth: 2 }}
onPress={(isChecked: boolean) => {
innerIconStyle={styles.permissionCheckBox}
onPress={() => {
onRequest();
}}
/>
Expand All @@ -29,6 +29,9 @@ const styles = StyleSheet.create({
alignItems: 'center',
paddingBottom: 5,
},
permissionCheckBox: {
borderWidth: 2,
},
permissionTitle: {
fontSize: 18,
fontWeight: '600',
Expand Down
12 changes: 12 additions & 0 deletions ios/MobilePaymentsSdkReactNative.mm
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,18 @@ @interface RCT_EXTERN_MODULE(MobilePaymentsSdkReactNative, NSObject)
RCT_EXTERN_METHOD(getSDKVersion:(RCTPromiseResolveBlock)resolve
withRejecter:(RCTPromiseRejectBlock)reject)

RCT_EXTERN_METHOD(linkAppleAccount:(RCTPromiseResolveBlock)resolve
withRejecter:(RCTPromiseRejectBlock)reject)

RCT_EXTERN_METHOD(relinkAppleAccount:(RCTPromiseResolveBlock)resolve
withRejecter:(RCTPromiseRejectBlock)reject)

RCT_EXTERN_METHOD(isAppleAccountLinked:(RCTPromiseResolveBlock)resolve
withRejecter:(RCTPromiseRejectBlock)reject)

RCT_EXTERN_METHOD(isDeviceCapable:(RCTPromiseResolveBlock)resolve
withRejecter:(RCTPromiseRejectBlock)reject)

+ (BOOL)requiresMainQueueSetup
{
return YES;
Expand Down
95 changes: 95 additions & 0 deletions ios/MobilePaymentsSdkReactNative.swift
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,101 @@ class MobilePaymentsSdkReactNative: RCTEventEmitter {
return resolve(mobilePaymentsSDK.settingsManager.sdkSettings.version)
}

func parseTapToPayError(error: NSError, defaultError: String) -> String {

let tapToPayReaderError = TapToPayReaderError(rawValue: (error).code)

let errorMessage: String
switch tapToPayReaderError {
case .alreadyLinked:
errorMessage = "Apple Tap to Pay Terms and Conditions have already been accepted."
case .banned:
errorMessage = "This device is banned from using the Tap To Pay reader."
case .linkingFailed:
errorMessage = "The Tap To Pay reader could not link/relink using the provided Apple ID."
case .linkingCanceled:
errorMessage = "User has canceled the linking/relinking operation."
case .invalidToken:
errorMessage = "The Tap To Pay reader generated an invalid token."
case .notAuthorized:
errorMessage = "This device must be authorized with a Square account in order to use Tap To Pay."
case .notAvailable:
errorMessage = "The Tap To Pay reader is not available on this device or device's operating system."
case .noNetwork:
errorMessage = "The Tap To Pay reader could not connect to the network. Please reconnect to the Internet and try again."
case .networkError:
errorMessage = "The network responded with an error."
case .other:
errorMessage = "An error with the Tap To Pay reader has occurred. Please try again."
case .passcodeDisabled:
errorMessage = "This device does not currently have an active passcode set."
case .unexpected:
errorMessage = "Mobile Payments SDK encountered an unexpected error. Please try again."
case .unsupportedOSVersion:
errorMessage = "The device's OS version does not meet the minimum requirement of iOS 16.7 for Tap to Pay on iPhone."
case .unsupportedDeviceModel:
errorMessage = "This device model is not currently supported to use the Tap To Pay reader."
default:
errorMessage = defaultError
}

return errorMessage

}

@objc(linkAppleAccount:withRejecter:)
func linkAppleAccount(resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
DispatchQueue.main.async { [weak self] in


self?.mobilePaymentsSDK.readerManager.tapToPaySettings.linkAppleAccount {error in

if (error != nil) {
let errorMessage = self?.parseTapToPayError(error: (error! as NSError), defaultError: "There has been an error linking apple account.")
reject("LINK_APPLE_ACCOUNT_ERROR", errorMessage, (error as? NSError)?.reactNativeError);
} else {
resolve("Apple account has been linked.")
}

}
}
}

@objc(relinkAppleAccount:withRejecter:)
func relinkAppleAccount(resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
DispatchQueue.main.async { [weak self] in
self?.mobilePaymentsSDK.readerManager.tapToPaySettings.relinkAppleAccount {error in

if (error != nil) {
let errorMessage = self?.parseTapToPayError(error: (error! as NSError), defaultError: "There has been an error re-linking apple account.")
reject("RE_LINK_APPLE_ACCOUNT_ERROR", errorMessage, (error as? NSError)?.reactNativeError);
} else {
resolve("Apple account has been re-linked.")
}

}
}
}

@objc(isAppleAccountLinked:withRejecter:)
func isAppleAccountLinked(resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
DispatchQueue.main.async { [weak self] in
self?.mobilePaymentsSDK.readerManager.tapToPaySettings.isAppleAccountLinked { isLinked, error in
if (error != nil) {
let errorMessage = self?.parseTapToPayError(error: (error! as NSError), defaultError: "There has been an error checking if Apple Account is Linked.")
reject("IS_APPLE_ACCOUNT_LINKED_ERROR", errorMessage, (error as? NSError)?.reactNativeError);
} else {
resolve(isLinked)
}
}
}
}

@objc(isDeviceCapable:withRejecter:)
func isDeviceCapable(resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) {
return resolve(mobilePaymentsSDK.readerManager.tapToPaySettings.isDeviceCapable)
}

/// Mock Readers, available only in Sandbox: https://developer.squareup.com/docs/mobile-payments-sdk/ios#mock-readers
private lazy var mockReaderUI: MockReaderUI? = {
guard mobilePaymentsSDK.settingsManager.sdkSettings.environment == .sandbox else {
Expand Down
2 changes: 1 addition & 1 deletion ios/Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ target 'RNMobilePaymentSDK' do
use_frameworks!

# Pods for RNMobilePaymentSDK
pod "SquareMobilePaymentsSDK", "~> 2.0.2"
pod "SquareMobilePaymentsSDK", "~> 2.1.0"
target 'RNMobilePaymentSDKTests' do
inherit! :search_paths
# Pods for testing
Expand Down
8 changes: 4 additions & 4 deletions ios/Podfile.lock
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
PODS:
- SquareMobilePaymentsSDK (2.0.2)
- SquareMobilePaymentsSDK (2.1.0)

DEPENDENCIES:
- SquareMobilePaymentsSDK (~> 2.0.2)
- SquareMobilePaymentsSDK (~> 2.1.0)

SPEC REPOS:
trunk:
- SquareMobilePaymentsSDK

SPEC CHECKSUMS:
SquareMobilePaymentsSDK: 1964169130cab2f447d767adf9c18f8b5d212ff6
SquareMobilePaymentsSDK: b437afc89530142af80ed818f4db02e07c47457c

PODFILE CHECKSUM: 1c0f22efdb235538a7a49119eaced2170ab7e369
PODFILE CHECKSUM: 36ec71d8ea5709b9c8c2277616eae29f2769b341

COCOAPODS: 1.16.2
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "mobile-payments-sdk-react-native",
"version": "2025.2.1",
"description": "Mobile Payments SDK plug-in for React Native. Enables developers to build secure in-person payment solutions.",
"mobilePaymentsSdkVersion": "2.0.2",
"mobilePaymentsSdkVersion": "2.1.0",
"source": "./src/index.tsx",
"main": "./lib/commonjs/index.js",
"module": "./lib/module/index.js",
Expand Down
35 changes: 35 additions & 0 deletions src/managers/reader.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Platform } from 'react-native';
import MobilePaymentsSdkReactNative from '../base_sdk';

export const startPairing = (): Promise<void> => {
Expand All @@ -15,3 +16,37 @@ export const showMockReaderUI = (): Promise<void> => {
export const hideMockReaderUI = (): Promise<void> => {
return MobilePaymentsSdkReactNative.hideMockReaderUI();
};

export namespace TapToPaySettings {
const notSupportedError = (): never => {
throw new Error('This feature is only available on iOS.');
};

export const linkAppleAccount = (): Promise<void> => {
return Platform.select({
ios: () => MobilePaymentsSdkReactNative.linkAppleAccount(),
android: () => Promise.reject(notSupportedError()),
})!();
};

export const relinkAppleAccount = (): Promise<void> => {
return Platform.select({
ios: () => MobilePaymentsSdkReactNative.relinkAppleAccount(),
android: () => Promise.reject(notSupportedError()),
})!();
};

export const isAppleAccountLinked = (): Promise<Boolean> => {
return Platform.select({
ios: () => MobilePaymentsSdkReactNative.isAppleAccountLinked(),
android: () => Promise.reject(notSupportedError()),
})!();
};

export const isDeviceCapable = (): Promise<Boolean> => {
return Platform.select({
ios: () => MobilePaymentsSdkReactNative.isDeviceCapable(),
android: () => Promise.reject(notSupportedError()),
})!();
};
}

0 comments on commit 04f3550

Please sign in to comment.