chore: prepare app store submission (screenshots, metadata, privacy details) (#1905)

This commit is contained in:
Hekmatullah 2025-08-18 18:11:10 +01:00 committed by GitHub
parent e8b7c94565
commit 4d200dc358
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
42 changed files with 483 additions and 45 deletions

View File

@ -116,7 +116,7 @@ jobs:
match_type: 'adhoc' match_type: 'adhoc'
provisioning_profile_name: 'match AdHoc org.mifospay' provisioning_profile_name: 'match AdHoc org.mifospay'
firebase_app_id: '1:728434912738:ios:86a7badfaed88b841a1dbb' firebase_app_id: '1:728434912738:ios:86a7badfaed88b841a1dbb'
metadata_path: './fastlane/metadata' metadata_path: './fastlane/metadata/ios'
use_cocoapods: true # <-- Set to true if using CocoaPods integration for KMP use_cocoapods: true # <-- Set to true if using CocoaPods integration for KMP
shared_module: ':cmp-shared' # <-- Gradle path to your shared KMP module (e.g., :shared) shared_module: ':cmp-shared' # <-- Gradle path to your shared KMP module (e.g., :shared)
distribute_ios_firebase: ${{ inputs.distribute_ios_firebase }} distribute_ios_firebase: ${{ inputs.distribute_ios_firebase }}

271
.github/workflows/sync-dirs.yaml vendored Normal file
View File

@ -0,0 +1,271 @@
name: Sync CMP Directories
on:
workflow_dispatch:
inputs:
upstream:
description: 'Upstream repository to sync directories from'
default: 'https://github.com/openMF/kmp-project-template.git'
required: true
type: string
schedule:
- cron: '0 0 * * 1'
jobs:
sync-directories:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: dev
- name: Setup Git config
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
- name: Add upstream remote and fetch
run: |
git remote add upstream ${{ inputs.upstream }} || true
git fetch upstream || exit 1
- name: Check upstream/dev exists
run: |
if ! git rev-parse --verify upstream/dev >/dev/null 2>&1; then
echo "Error: upstream/dev branch does not exist"
exit 1
fi
- name: Create and checkout temporary branch
run: |
TEMP_BRANCH="temp-sync-branch-${{ github.run_number }}"
git checkout -b "$TEMP_BRANCH" upstream/dev || exit 1
echo "TEMP_BRANCH=$TEMP_BRANCH" >> $GITHUB_ENV
- name: Sync directories and files
run: |
# Declare directories and files to sync
DIRS=(
"cmp-android"
"cmp-desktop"
"cmp-ios"
"cmp-web"
"cmp-shared"
"core-base"
"build-logic"
"fastlane"
"scripts"
"config"
".github"
".run"
)
FILES=(
"Gemfile"
"Gemfile.lock"
"ci-prepush.bat"
"ci-prepush.sh"
)
# Define exclusions
declare -A EXCLUSIONS=(
["cmp-android"]="src/main/res dependencies src/main/ic_launcher-playstore.png google-services.json"
["cmp-web"]="src/jsMain/resources src/wasmJsMain/resources"
["cmp-desktop"]="icons"
["cmp-ios"]="iosApp/Assets.xcassets"
["root"]="secrets.env"
)
# Function to check if path should be excluded
should_exclude() {
local dir=$1
local path=$2
# Check for root exclusions
if [[ "$dir" == "." && -n "${EXCLUSIONS["root"]}" ]]; then
local root_excluded_paths=(${EXCLUSIONS["root"]})
for excluded in "${root_excluded_paths[@]}"; do
if [[ "$path" == *"$excluded"* ]]; then
return 0
fi
done
fi
# Check directory-specific exclusions
if [[ -n "${EXCLUSIONS[$dir]}" ]]; then
local excluded_paths=(${EXCLUSIONS[$dir]})
for excluded in "${excluded_paths[@]}"; do
if [[ "$path" == *"$excluded"* ]]; then
return 0
fi
done
fi
return 1
}
# Function to preserve excluded paths
preserve_excluded() {
local dir=$1
if [[ -n "${EXCLUSIONS[$dir]}" ]]; then
local excluded_paths=(${EXCLUSIONS[$dir]})
for excluded in "${excluded_paths[@]}"; do
local full_path="$dir/$excluded"
if [[ -e "$full_path" ]]; then
echo "Preserving excluded path: $full_path"
local temp_path="temp_excluded/$full_path"
mkdir -p "$(dirname "$temp_path")"
cp -r "$full_path" "$(dirname "$temp_path")"
fi
done
fi
}
# Function to restore excluded paths
restore_excluded() {
local dir=$1
if [[ -n "${EXCLUSIONS[$dir]}" ]]; then
local excluded_paths=(${EXCLUSIONS[$dir]})
for excluded in "${excluded_paths[@]}"; do
local full_path="$dir/$excluded"
local temp_path="temp_excluded/$full_path"
if [[ -e "$temp_path" ]]; then
echo "Restoring excluded path: $full_path"
mkdir -p "$(dirname "$full_path")"
rm -rf "$full_path"
cp -r "$temp_path" "$(dirname "$full_path")"
fi
done
fi
}
# Function to preserve root-level excluded files
preserve_root_files() {
if [[ -n "${EXCLUSIONS["root"]}" ]]; then
local excluded_paths=(${EXCLUSIONS["root"]})
for excluded in "${excluded_paths[@]}"; do
if [[ -e "$excluded" ]]; then
echo "Preserving root-level excluded file: $excluded"
mkdir -p "temp_excluded/root"
cp -r "$excluded" "temp_excluded/root/"
fi
done
fi
}
# Function to restore root-level excluded files
restore_root_files() {
if [[ -n "${EXCLUSIONS["root"]}" ]]; then
local excluded_paths=(${EXCLUSIONS["root"]})
for excluded in "${excluded_paths[@]}"; do
if [[ -e "temp_excluded/root/$excluded" ]]; then
echo "Restoring root-level excluded file: $excluded"
cp -r "temp_excluded/root/$excluded" "./"
fi
done
fi
}
# Create temp directory for exclusions
mkdir -p temp_excluded
# Preserve root-level exclusions before sync
preserve_root_files
# Switch to dev branch
git checkout dev
# Sync directories
for dir in "${DIRS[@]}"; do
if [ ! -d "$dir" ]; then
echo "Creating $dir..."
mkdir -p "$dir"
fi
# Preserve excluded paths before sync
if [[ -d "$dir" ]]; then
preserve_excluded "$dir"
fi
echo "Syncing $dir..."
git checkout "${{ env.TEMP_BRANCH }}" -- "$dir" || exit 1
# Restore excluded paths after sync
restore_excluded "$dir"
done
# Sync files
for file in "${FILES[@]}"; do
dir=$(dirname "$file")
if ! should_exclude "$dir" "$file"; then
echo "Syncing $file..."
git checkout "${{ env.TEMP_BRANCH }}" -- "$file" || true
else
echo "Skipping excluded file: $file"
fi
done
# Restore root-level excluded files
restore_root_files
# Cleanup temp directory
rm -rf temp_excluded
- name: Clean up temporary branch
if: always()
run: git branch -D "${{ env.TEMP_BRANCH }}" || true
- name: Check for changes
id: check_changes
run: |
if [[ -n "$(git status --porcelain)" ]]; then
echo "has_changes=true" >> $GITHUB_OUTPUT
else
echo "has_changes=false" >> $GITHUB_OUTPUT
fi
- name: Create Pull Request
if: steps.check_changes.outputs.has_changes == 'true'
uses: peter-evans/create-pull-request@v7
with:
token: ${{ secrets.PAT_TOKEN }}
commit-message: "chore: Sync directories and files from upstream"
title: "chore: Sync directories and files from upstream"
body: |
Automated sync of directories and files from upstream repository.
Changes included in this sync:
Directories:
- cmp-android (excluding src/main/res, dependencies, ic_launcher-playstore.png, google-services.json)
- cmp-desktop (excluding icons)
- cmp-ios (excluding iosApp/Assets.xcassets)
- cmp-web (excluding src/jsMain/resources, src/wasmJsMain/resources)
- cmp-shared
- build-logic
- fastlane
- scripts
- config
- .github
- .run
Files:
- Gemfile
- Gemfile.lock
- ci-prepush.bat
- ci-prepush.sh
Root-level exclusions:
- secrets.env
Workflow run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
branch: sync-dirs-${{ github.run_number }}
delete-branch: true
labels: |
sync
automated pr
base: dev

View File

@ -353,8 +353,8 @@
baseConfigurationReference = E5D357E5C5AAADD27F979C77 /* Pods-iosApp.debug.xcconfig */; baseConfigurationReference = E5D357E5C5AAADD27F979C77 /* Pods-iosApp.debug.xcconfig */;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "Apple Distribution"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Manual; CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\""; DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\"";
DEVELOPMENT_TEAM = L432S2FZP5; DEVELOPMENT_TEAM = L432S2FZP5;
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
@ -373,7 +373,7 @@
MARKETING_VERSION = 1.0.0; MARKETING_VERSION = 1.0.0;
PRODUCT_BUNDLE_IDENTIFIER = org.mifospay; PRODUCT_BUNDLE_IDENTIFIER = org.mifospay;
PRODUCT_NAME = "${APP_NAME}"; PRODUCT_NAME = "${APP_NAME}";
PROVISIONING_PROFILE_SPECIFIER = "match AdHoc org.mifospay"; PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
}; };
@ -384,8 +384,8 @@
baseConfigurationReference = 471D4B8AF5995E32718DCCCD /* Pods-iosApp.release.xcconfig */; baseConfigurationReference = 471D4B8AF5995E32718DCCCD /* Pods-iosApp.release.xcconfig */;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "Apple Distribution"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Manual; CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\""; DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\"";
DEVELOPMENT_TEAM = L432S2FZP5; DEVELOPMENT_TEAM = L432S2FZP5;
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
@ -404,7 +404,7 @@
MARKETING_VERSION = 1.0.0; MARKETING_VERSION = 1.0.0;
PRODUCT_BUNDLE_IDENTIFIER = org.mifospay; PRODUCT_BUNDLE_IDENTIFIER = org.mifospay;
PRODUCT_NAME = "${APP_NAME}"; PRODUCT_NAME = "${APP_NAME}";
PROVISIONING_PROFILE_SPECIFIER = "match AdHoc org.mifospay"; PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
}; };

View File

@ -17,11 +17,15 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string> <string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>1.0</string> <string>1.0.0</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>6</string> <string>8</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>NSCameraUsageDescription</key>
<string>We use the camera to scan QR codes to send and receive payments.</string>
<key>NSPhotoLibraryAddUsageDescription</key> <key>NSPhotoLibraryAddUsageDescription</key>
<string>Allow access to add photos to your library so you can save artworks directly to your device and view them offline.</string> <string>Allow access to add photos to your library so you can save artworks directly to your device and view them offline.</string>
<key>UIApplicationSceneManifest</key> <key>UIApplicationSceneManifest</key>

View File

@ -28,8 +28,8 @@ module FastlaneConfig
key_id: "7V3ABCDEFG", key_id: "7V3ABCDEFG",
issuer_id: "7ab9e231-9603-4c3e-a147-be3b0f123456", issuer_id: "7ab9e231-9603-4c3e-a147-be3b0f123456",
key_filepath: "./secrets/Auth_key.p8", key_filepath: "./secrets/Auth_key.p8",
version_number: "1.0", version_number: "1.0.0",
metadata_path: "./fastlane/metadata", metadata_path: "./fastlane/metadata/ios",
app_rating_config_path: "./fastlane/age_rating.json" app_rating_config_path: "./fastlane/age_rating.json"
} }
end end

View File

@ -388,6 +388,16 @@ platform :ios do
) )
end end
private_lane :set_plist_values do
ios_config = FastlaneConfig::IosConfig::BUILD_CONFIG
update_plist(
plist_path: ios_config[:plist_path],
block: proc do |plist|
plist["NSCameraUsageDescription"] = "We use the camera to scan QR codes to send and receive payments."
end
)
end
################### ###################
# Main Public lanes # Main Public lanes
################### ###################
@ -499,6 +509,8 @@ platform :ios do
build_number: latest_build_number + 1 build_number: latest_build_number + 1
) )
set_plist_values
build_ios_project( build_ios_project(
options.merge( options.merge(
provisioning_profile_name: "match AppStore org.mifospay" provisioning_profile_name: "match AppStore org.mifospay"
@ -527,7 +539,7 @@ platform :ios do
version_number: ios_config[:version_number] version_number: ios_config[:version_number]
) )
latest_build_number = app_store_build_number( latest_build_number = latest_testflight_build_number(
app_identifier: options[:app_identifier] || ios_config[:app_identifier], app_identifier: options[:app_identifier] || ios_config[:app_identifier],
api_key: Actions.lane_context[SharedValues::APP_STORE_CONNECT_API_KEY], api_key: Actions.lane_context[SharedValues::APP_STORE_CONNECT_API_KEY],
version: ios_config[:version_number] version: ios_config[:version_number]
@ -538,14 +550,7 @@ platform :ios do
build_number: latest_build_number + 1 build_number: latest_build_number + 1
) )
update_plist( set_plist_values
plist_path: ios_config[:plist_path],
block: proc do |plist|
plist['NSContactsUsageDescription'] = 'This app does not access your contacts. This message is required for compliance only.'
plist['NSLocationWhenInUseUsageDescription'] = 'This app does not access your location. This message is required for compliance only.'
plist['NSBluetoothAlwaysUsageDescription'] = 'This app does not use Bluetooth. This message is required for compliance only.'
end
)
build_ios_project( build_ios_project(
options.merge( options.merge(
@ -555,10 +560,10 @@ platform :ios do
deliver( deliver(
metadata_path: options[:metadata_path] || ios_config[:metadata_path], metadata_path: options[:metadata_path] || ios_config[:metadata_path],
submit_for_review: true, # Set to true if you want to auto-submit for review submit_for_review: false, # Set to true if you want to auto-submit for review
automatic_release: true, # Set to true if you want to auto-release once it approved automatic_release: true, # Set to true if you want to auto-release once it approved
api_key: Actions.lane_context[SharedValues::APP_STORE_CONNECT_API_KEY], api_key: Actions.lane_context[SharedValues::APP_STORE_CONNECT_API_KEY],
skip_app_version_update: true, skip_app_version_update: false,
force: true, # Skips HTML report verification force: true, # Skips HTML report verification
precheck_include_in_app_purchases: false, precheck_include_in_app_purchases: false,
overwrite_screenshots: true, overwrite_screenshots: true,

16
fastlane/age_rating.json Normal file
View File

@ -0,0 +1,16 @@
{
"violenceCartoonOrFantasy": "NONE",
"violenceRealistic": "NONE",
"violenceRealisticProlongedGraphicOrSadistic": "NONE",
"profanityOrCrudeHumor": "NONE",
"matureOrSuggestiveThemes": "NONE",
"horrorOrFearThemes": "NONE",
"sexualContentGraphicAndNudity": "NONE",
"alcoholTobaccoOrDrugUseOrReferences": "NONE",
"gamblingSimulated": "NONE",
"sexualContentOrNudity": "NONE",
"contests": "NONE",
"medicalOrTreatmentInformation": "NONE",
"unrestrictedWebAccess": false,
"gambling": false
}

View File

@ -0,0 +1,101 @@
[
{
"category": "CONTACTS",
"purposes": [
"APP_FUNCTIONALITY"
],
"data_protections": [
"DATA_LINKED_TO_YOU"
]
},
{
"category": "CREDIT_AND_FRAUD",
"purposes": [
"APP_FUNCTIONALITY"
],
"data_protections": [
"DATA_LINKED_TO_YOU"
]
},
{
"category": "DEVICE_ID",
"purposes": [
"APP_FUNCTIONALITY"
],
"data_protections": [
"DATA_LINKED_TO_YOU"
]
},
{
"category": "EMAIL_ADDRESS",
"purposes": [
"APP_FUNCTIONALITY"
],
"data_protections": [
"DATA_LINKED_TO_YOU"
]
},
{
"category": "NAME",
"purposes": [
"APP_FUNCTIONALITY"
],
"data_protections": [
"DATA_LINKED_TO_YOU"
]
},
{
"category": "OTHER_FINANCIAL_INFO",
"purposes": [
"APP_FUNCTIONALITY"
],
"data_protections": [
"DATA_LINKED_TO_YOU"
]
},
{
"category": "PAYMENT_INFORMATION",
"purposes": [
"APP_FUNCTIONALITY"
],
"data_protections": [
"DATA_LINKED_TO_YOU"
]
},
{
"category": "PHONE_NUMBER",
"purposes": [
"APP_FUNCTIONALITY"
],
"data_protections": [
"DATA_LINKED_TO_YOU"
]
},
{
"category": "PHOTOS_OR_VIDEOS",
"purposes": [
"APP_FUNCTIONALITY"
],
"data_protections": [
"DATA_LINKED_TO_YOU"
]
},
{
"category": "PHYSICAL_ADDRESS",
"purposes": [
"APP_FUNCTIONALITY"
],
"data_protections": [
"DATA_LINKED_TO_YOU"
]
},
{
"category": "USER_ID",
"purposes": [
"APP_FUNCTIONALITY"
],
"data_protections": [
"DATA_LINKED_TO_YOU"
]
}
]

View File

@ -0,0 +1,3 @@
© 2025 The Mifos Initiative
This app is licensed under the Mozilla Public License, v. 2.0.

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,16 @@
Mifos Pay is a simple, secure way to send, request, and track money. Create a wallet account, add a card, share a QR to get paid, set standing instructions for recurring transfers, and manage invoices—all from one clean, modern app.
WHAT YOU CAN DO
• Sign up as a customer or merchant and complete a guided registration
• Create a wallet savings account (subject to approval) and see your balance at a glance
• Send money using a Virtual Payment Address (VPA), mobile number, or account number
• Request money with your personal QR code; optionally set the amount and currency before sharing
• Add and manage cards; view masked details and remove or edit when needed
• Track everything in Payments: filter History by debits or credits, view statements, and see real-time notifications
• Set SI (Standing Instructions) for automatic transfers on a fixed schedule (weeks, months, years)
• Create and review invoices; see invoice status, items, and transaction IDs
• Keep profile details up to date—name, address, phone, VPA—and link a bank account
• Upload documents for KYC or support directly in the app
• Get help fast with built-in FAQs

View File

@ -0,0 +1 @@
wallet,payments,transfer,sendmoney,QRpay,invoice,card,linkbank,KYC,merchant

View File

@ -0,0 +1 @@

View File

@ -0,0 +1 @@
Mifos Pay

View File

@ -0,0 +1 @@
https://openmf.github.io/privacy_policy_mifos_mobile.html

View File

@ -0,0 +1 @@
Send and receive money, pay merchants, and track transactions with an open-source wallet powered by Mifos X / Fineract.

View File

@ -0,0 +1 @@

View File

@ -0,0 +1 @@
Open-source digital wallet

View File

@ -0,0 +1 @@
https://github.com/openMF/mobile-wallet

View File

@ -0,0 +1 @@
Finance

View File

@ -0,0 +1 @@

View File

@ -0,0 +1 @@

View File

@ -0,0 +1 @@
@Venus@2025@

View File

@ -0,0 +1 @@
venus

View File

@ -0,0 +1 @@
info@mifos.org

View File

@ -0,0 +1 @@
Hekmatullah

View File

@ -0,0 +1 @@
Amin

View File

@ -0,0 +1 @@

View File

@ -0,0 +1 @@
+44 7391462187

View File

@ -0,0 +1 @@
Utilities

View File

@ -0,0 +1 @@

View File

@ -0,0 +1 @@

Binary file not shown.

After

Width:  |  Height:  |  Size: 734 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 873 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 727 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 523 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 482 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1023 KiB