Closed kuldip-simform closed 5 months ago
The issue was from my side I forgot to add build phase which is mentioned here in iOS SDK documentation.
But as I am using Expo and there is no plugins provided by Square, I have to create my own to add merchantIds in xcode and enable google pay in android side.also I had to create plugin to add build phase in xcode project build phase. Now issue arise due to order of build phase.
All the plugins run before pod install in expo and after pod install [CP] Embed Pods Frameworks
script gets added. whereas square script needs to last.
So I had to right ruby script to re-order build script to be last and run this script in eas-build-post-install
script. so this will be run after pod-install is done.
Re-ordering build phase script
#!/usr/bin/env ruby
require 'xcodeproj'
require 'set'
project_file, target_name = ARGV
puts "Sorting sources in #{project_file} for target #{target_name}"
project = Xcodeproj::Project.open(project_file)
target = project.targets.select { |t| t.name == target_name }.first
square_framework_run_script_index = target.build_phases.index { |b|
name = b.name if b.respond_to? :name
name == "Square Framework Run Script - InAppPaymentsSDK" #name of build phase you have given in plugin below
}
puts "Square Framework Run Script index: #{square_framework_run_script_index} Total build phases: #{target.build_phases.count - 1}"
if square_framework_run_script_index.nil? == false
puts "Moving Square Framework Run Script from #{square_framework_run_script_index} to the #{target.build_phases.count - 1} index"
target.build_phases.move_from(square_framework_run_script_index, target.build_phases.count - 1) # move to the last indexs
end
project.save
Plugin for adding script in Xcode
const { withXcodeProject } = require('@expo/config-plugins');
const addBuildPhaseForSquareIOS = (config) => {
return withXcodeProject(config, async (conf) => {
const project = conf.modResults;
project.addBuildPhase(
[],
'PBXShellScriptBuildPhase',
'Square Framework Run Script - InAppPaymentsSDK', // build phase name
project.getFirstTarget().uuid,
{
shellPath: '/bin/sh',
shellScript:
'FRAMEWORKS="${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}" && "${FRAMEWORKS}/SquareInAppPaymentsSDK.framework/setup"',
},
);
return conf;
});
};
module.exports = addBuildPhaseForSquareIOS;
Add this plugin in app.json
or app.config.ts
.
like this
{
"expo": {
...,
plugins: [
...,
[
"./appleGooglePayConfigPlugin.js",
{
"merchantIdentifier": "your merchant id",
"enableGooglePay": true
}
],
[
"./squareIOSBuildPhasePlugin.js"
],
],
}
}
script file for adding that to package.json like: "eas-build-post-install": "./eas-hooks/eas-build-post-install.sh"
#!/usr/bin/env bash
# This is a file called "pre-install" in the root of the project
if [[ "$EAS_BUILD_PLATFORM" == "ios" ]]; then
./reorderSquareIAPBuildPhase.rb ios/your_project_name.xcodeproj your_target_name
fi
I have also written another plugin for adding merchantsId and google pay to true.
const {
AndroidConfig,
ConfigPlugin,
IOSConfig,
withAndroidManifest,
withEntitlementsPlist,
} = require('@expo/config-plugins');
const { addMetaDataItemToMainApplication, getMainApplicationOrThrow, removeMetaDataItemFromMainApplication } =
AndroidConfig.Manifest;
// type SquarePluginProps = {
// /**
// * The iOS merchant ID used for enabling Apple Pay.
// * Without this, the error "Missing merchant identifier" will be thrown on iOS.
// */
// merchantIdentifier: string | string[];
// enableGooglePay: boolean;
// };
const withSquareIos = (expoConfig, { merchantIdentifier }) => {
return withEntitlementsPlist(expoConfig, (config) => {
config.modResults = setApplePayEntitlement(merchantIdentifier, config.modResults);
return config;
});
};
const withSquare = (config, props) => {
config = withSquareIos(config, props);
config = withNoopSwiftFile(config);
config = withSquareAndroid(config, props);
return config;
};
/**
* Adds the following to the entitlements:
*
* <key>com.apple.developer.in-app-payments</key>
* <array>
* <string>[MERCHANT_IDENTIFIER]</string>
* </array>
*/
function setApplePayEntitlement(merchantIdentifiers, entitlements) {
const key = 'com.apple.developer.in-app-payments';
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const merchants = entitlements[key] ?? [];
if (!Array.isArray(merchantIdentifiers)) {
merchantIdentifiers = [merchantIdentifiers];
}
for (const id of merchantIdentifiers) {
if (id && !merchants.includes(id)) {
merchants.push(id);
}
}
if (merchants.length) {
entitlements[key] = merchants;
}
return entitlements;
}
/**
* Add a blank Swift file to the Xcode project for Swift compatibility.
*/
const withNoopSwiftFile = (config) => {
return IOSConfig.XcodeProjectFile.withBuildSourceFile(config, {
filePath: 'noop-file.swift',
contents: [
'//',
'// @generated',
'// A blank Swift file must be created for native modules with Swift files to work correctly.',
'//',
'',
].join('\n'),
});
};
const withSquareAndroid = (expoConfig, { enableGooglePay = false }) => {
return withAndroidManifest(expoConfig, (config) => {
config.modResults = setGooglePayMetaData(enableGooglePay, config.modResults);
return config;
});
};
/**
* Adds the following to AndroidManifest.xml:
*
* <application>
* ...
* <meta-data
* android:name="com.google.android.gms.wallet.api.enabled"
* android:value="true|false" />
* </application>
*/
function setGooglePayMetaData(enabled, modResults) {
const GOOGLE_PAY_META_NAME = 'com.google.android.gms.wallet.api.enabled';
const mainApplication = getMainApplicationOrThrow(modResults);
if (enabled) {
addMetaDataItemToMainApplication(mainApplication, GOOGLE_PAY_META_NAME, 'true');
} else {
removeMetaDataItemFromMainApplication(mainApplication, GOOGLE_PAY_META_NAME);
}
return modResults;
}
module.exports = withSquare;
@kuldip-simform Your comment has been incredibly helpful. Thank you for sharing your work! 🙏
Thank you @timmyjose . Let's hope we get first party support from square for this so not everyone has to write this themselves.
@kuldip-simform Sorry to bother you, but this is something that I'm not able to figure out (relatively new to React Native and frontend). Have you by chance encountered this error while invoking something like:
await SQIPApplePay.requestApplePayNonce(
{
price: '1.00',
summaryLabel: 'Test Item',
countryCode: 'US',
currencyCode: 'USD',
paymentType: SQIPApplePay.PaymentTypeFinal,
},
onApplePayRequestNonceSuccess,
onApplePayRequestNonceFailure,
onApplePayComplete,
)
Error:
[UIKitCore] Attempt to present <PKPaymentAuthorizationViewController: 0x1308094b0> on <UIViewController: 0x10831cb60> (from <UIViewController: 0x10831cb60>) which is already presenting <RNSScreen: 0x17f2fcc00>.
I've been trying to look this up, but even without any modal (from what I understand about the error message), I still see this for iOS
, and it's become a blocker. 🙁
(If you haven't seen this, no worries - your plugin code has already been super helpful for us, and saved us a lot of time and effort! 🙏)
@timmyjose Fortunately I have not encounter this error (😀) so I can't help you in this problem.
Yes, you are right in understanding that this error would be due to you are presenting another modal screen on top of one.
@kuldip-simform Thank you for affirming that that is the issue - yes, I suppose it is very much because of the way the UI in my app is structured. Will look into simplifying it. Glad to hear that you didn't run into this issue, and thank you for all the help all the same! 🙂
The issue was from my side I forgot to add build phase which is mentioned here in iOS SDK documentation.
But as I am using Expo and there is no plugins provided by Square, I have to create my own to add merchantIds in xcode and enable google pay in android side.also I had to create plugin to add build phase in xcode project build phase. Now issue arise due to order of build phase.
All the plugins run before pod install in expo and after pod install
[CP] Embed Pods Frameworks
script gets added. whereas square script needs to last.So I had to right ruby script to re-order build script to be last and run this script in
eas-build-post-install
script. so this will be run after pod-install is done.Re-ordering build phase script
#!/usr/bin/env ruby require 'xcodeproj' require 'set' project_file, target_name = ARGV puts "Sorting sources in #{project_file} for target #{target_name}" project = Xcodeproj::Project.open(project_file) target = project.targets.select { |t| t.name == target_name }.first square_framework_run_script_index = target.build_phases.index { |b| name = b.name if b.respond_to? :name name == "Square Framework Run Script - InAppPaymentsSDK" #name of build phase you have given in plugin below } puts "Square Framework Run Script index: #{square_framework_run_script_index} Total build phases: #{target.build_phases.count - 1}" if square_framework_run_script_index.nil? == false puts "Moving Square Framework Run Script from #{square_framework_run_script_index} to the #{target.build_phases.count - 1} index" target.build_phases.move_from(square_framework_run_script_index, target.build_phases.count - 1) # move to the last indexs end project.save
Plugin for adding script in Xcode
const { withXcodeProject } = require('@expo/config-plugins'); const addBuildPhaseForSquareIOS = (config) => { return withXcodeProject(config, async (conf) => { const project = conf.modResults; project.addBuildPhase( [], 'PBXShellScriptBuildPhase', 'Square Framework Run Script - InAppPaymentsSDK', // build phase name project.getFirstTarget().uuid, { shellPath: '/bin/sh', shellScript: 'FRAMEWORKS="${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}" && "${FRAMEWORKS}/SquareInAppPaymentsSDK.framework/setup"', }, ); return conf; }); }; module.exports = addBuildPhaseForSquareIOS;
Add this plugin in
app.json
orapp.config.ts
. like this{ "expo": { ..., plugins: [ ..., [ "./appleGooglePayConfigPlugin.js", { "merchantIdentifier": "your merchant id", "enableGooglePay": true } ], [ "./squareIOSBuildPhasePlugin.js" ], ], } }
script file for adding that to package.json like:
"eas-build-post-install": "./eas-hooks/eas-build-post-install.sh"
#!/usr/bin/env bash # This is a file called "pre-install" in the root of the project if [[ "$EAS_BUILD_PLATFORM" == "ios" ]]; then ./reorderSquareIAPBuildPhase.rb ios/your_project_name.xcodeproj your_target_name fi
I have also written another plugin for adding merchantsId and google pay to true.
const { AndroidConfig, ConfigPlugin, IOSConfig, withAndroidManifest, withEntitlementsPlist, } = require('@expo/config-plugins'); const { addMetaDataItemToMainApplication, getMainApplicationOrThrow, removeMetaDataItemFromMainApplication } = AndroidConfig.Manifest; // type SquarePluginProps = { // /** // * The iOS merchant ID used for enabling Apple Pay. // * Without this, the error "Missing merchant identifier" will be thrown on iOS. // */ // merchantIdentifier: string | string[]; // enableGooglePay: boolean; // }; const withSquareIos = (expoConfig, { merchantIdentifier }) => { return withEntitlementsPlist(expoConfig, (config) => { config.modResults = setApplePayEntitlement(merchantIdentifier, config.modResults); return config; }); }; const withSquare = (config, props) => { config = withSquareIos(config, props); config = withNoopSwiftFile(config); config = withSquareAndroid(config, props); return config; }; /** * Adds the following to the entitlements: * * <key>com.apple.developer.in-app-payments</key> * <array> * <string>[MERCHANT_IDENTIFIER]</string> * </array> */ function setApplePayEntitlement(merchantIdentifiers, entitlements) { const key = 'com.apple.developer.in-app-payments'; // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const merchants = entitlements[key] ?? []; if (!Array.isArray(merchantIdentifiers)) { merchantIdentifiers = [merchantIdentifiers]; } for (const id of merchantIdentifiers) { if (id && !merchants.includes(id)) { merchants.push(id); } } if (merchants.length) { entitlements[key] = merchants; } return entitlements; } /** * Add a blank Swift file to the Xcode project for Swift compatibility. */ const withNoopSwiftFile = (config) => { return IOSConfig.XcodeProjectFile.withBuildSourceFile(config, { filePath: 'noop-file.swift', contents: [ '//', '// @generated', '// A blank Swift file must be created for native modules with Swift files to work correctly.', '//', '', ].join('\n'), }); }; const withSquareAndroid = (expoConfig, { enableGooglePay = false }) => { return withAndroidManifest(expoConfig, (config) => { config.modResults = setGooglePayMetaData(enableGooglePay, config.modResults); return config; }); }; /** * Adds the following to AndroidManifest.xml: * * <application> * ... * <meta-data * android:name="com.google.android.gms.wallet.api.enabled" * android:value="true|false" /> * </application> */ function setGooglePayMetaData(enabled, modResults) { const GOOGLE_PAY_META_NAME = 'com.google.android.gms.wallet.api.enabled'; const mainApplication = getMainApplicationOrThrow(modResults); if (enabled) { addMetaDataItemToMainApplication(mainApplication, GOOGLE_PAY_META_NAME, 'true'); } else { removeMetaDataItemFromMainApplication(mainApplication, GOOGLE_PAY_META_NAME); } return modResults; } module.exports = withSquare;
You can also move the ruby script to a config plugin:
const { withXcodeProject } = require("@expo/config-plugins");
const withReorderSquareBuildPhase = (config) => {
return withXcodeProject(config, async (config) => {
const xcodeProject = config.modResults;
const target = xcodeProject.getFirstTarget().firstTarget;
const targetName = target.name;
console.log(
`[withReorderSquareBuildPhase] Reordering build phases for target ${targetName}...`,
);
const squareFrameworkRunScriptIndex = target.buildPhases?.findIndex(
(buildPhase) => {
const buildPhaseName = buildPhase.name;
return buildPhaseName === "Configure SQIP SDK for iOS";
},
);
if (squareFrameworkRunScriptIndex !== -1) {
console.log(
`[withReorderSquareBuildPhase] Moving Square Framework Run Script from ${squareFrameworkRunScriptIndex} to the ${xcodeProject.pbxNativeTargetSection(targetName).buildPhases.length - 1} index`,
);
target.buildPhases.move(
squareFrameworkRunScriptIndex,
xcodeProject.pbxNativeTargetSection(targetName).buildPhases.length - 1,
);
}
return config;
});
};
module.exports = withReorderSquareBuildPhase;
Describe the issue
I have updated to new version
1.7.4
from1.7.2
. After that When I open app, app immediately crashes on both real device and simulatorHere is the log:
To Reproduce
Steps to reproduce the issue.
For example -
Expected behavior
app should run without any issue.
Environment (please complete the following information):
Screenshots
https://github.com/square/in-app-payments-react-native-plugin/assets/104821076/bd969b21-f3c9-4216-a644-42ae2942e4d9